You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by ka...@apache.org on 2010/06/26 20:08:58 UTC

svn commit: r958264 - in /db/derby/code/trunk/java/testing/org/apache/derbyTesting: functionTests/tests/engine/LockInterruptTest.java functionTests/tests/engine/_Suite.java junit/BaseTestCase.java

Author: kahatlen
Date: Sat Jun 26 18:08:57 2010
New Revision: 958264

URL: http://svn.apache.org/viewvc?rev=958264&view=rev
Log:
DERBY-4711: Hung thread after another thread is interrupted

Added JUnit test case.

Added:
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/LockInterruptTest.java   (with props)
Modified:
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/_Suite.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseTestCase.java

Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/LockInterruptTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/LockInterruptTest.java?rev=958264&view=auto
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/LockInterruptTest.java (added)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/LockInterruptTest.java Sat Jun 26 18:08:57 2010
@@ -0,0 +1,157 @@
+/*
+ * Derby - Class org.apache.derbyTesting.functionTests.tests.engine.LockInterruptTest
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.derbyTesting.functionTests.tests.engine;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+import junit.framework.Test;
+import org.apache.derbyTesting.junit.BaseJDBCTestCase;
+import org.apache.derbyTesting.junit.CleanDatabaseTestSetup;
+import org.apache.derbyTesting.junit.DatabasePropertyTestSetup;
+import org.apache.derbyTesting.junit.JDBC;
+import org.apache.derbyTesting.junit.TestConfiguration;
+
+/**
+ * Test that other threads are able to proceed if one thread is interrupted
+ * when it's waiting for a lock. Regression test case for DERBY-4711.
+ */
+public class LockInterruptTest extends BaseJDBCTestCase {
+    /** SQLState for exception thrown because of interrupts. */
+    private final static String INTERRUPTED = "08000";
+
+    /** Lock timeout in seconds used in this test. */
+    private final static int LOCK_TIMEOUT = 60;
+
+    /** Deadlock timeout in seconds used in this test. */
+    private final static int DEADLOCK_TIMEOUT = LOCK_TIMEOUT / 2;
+
+    public LockInterruptTest(String name) {
+        super(name);
+    }
+
+    public static Test suite() {
+        // Only run in embedded mode since we cannot interrupt the engine
+        // thread from the network client.
+        Test test = TestConfiguration.embeddedSuite(LockInterruptTest.class);
+
+        // Set the lock timeout to a known value so that we know what to
+        // expect for timeouts.
+        test = DatabasePropertyTestSetup.setLockTimeouts(
+                test, DEADLOCK_TIMEOUT, LOCK_TIMEOUT);
+
+        return new CleanDatabaseTestSetup(test);
+    }
+
+    public void testInterruptLockWaiter() throws Exception {
+        setAutoCommit(false);
+        Statement s = createStatement();
+        s.executeUpdate("create table derby4711(x int)");
+        commit();
+
+        // Obtain a table lock in order to block the waiter threads.
+        s.executeUpdate("lock table derby4711 in share mode");
+
+        // Create first waiter thread.
+        Waiter t1 = new Waiter();
+        t1.start();
+        Thread.sleep(2000); // give t1 time to become the first waiter
+
+        // Create second waiter thread.
+        Waiter t2 = new Waiter();
+        t2.start();
+        Thread.sleep(2000); // give t2 time to enter the wait queue
+
+        // Now that the queue of waiters has been set up, interrupt the
+        // first thread.
+        t1.interrupt();
+
+        // Release the table lock to allow the waiters to proceed.
+        commit();
+
+        // Wait for the threads to complete before checking their state.
+        t1.join();
+        t2.join();
+
+        // The first thread should fail because it was interrupted.
+        Throwable e1 = t1.throwable;
+        assertNotNull("First thread should fail because of interrupt", e1);
+        if (!(e1 instanceof SQLException)) {
+            fail("Unexpected exception from first thread", e1);
+        }
+        assertSQLState(INTERRUPTED, (SQLException) e1);
+
+        // The second thread should be able to complete successfully.
+        Throwable e2 = t2.throwable;
+        if (e2 != null) {
+            fail("Unexpected exception from second thread", e2);
+        }
+
+        // And the second thread should be able to complete in less time than
+        // the deadlock timeout (before DERBY-4711, it would wait for a
+        // timeout before obtaining the lock, even if the lock was available
+        // long before).
+        if (t2.elapsedTime >= DEADLOCK_TIMEOUT * 1000) {
+            fail("Second thread needed " + t2.elapsedTime +
+                 " ms to complete. Probably stuck waiting for a lock.");
+        }
+
+        // Expect that the second thread managed to insert a row.
+        JDBC.assertSingleValueResultSet(
+                s.executeQuery("select * from derby4711"), "1");
+    }
+
+    /**
+     * Thread class that opens a new connection and attempts to insert a
+     * row into a table.
+     */
+    private class Waiter extends Thread {
+        private final Connection c;
+        private final PreparedStatement ps;
+
+        private Throwable throwable;
+        private long elapsedTime;
+
+        private Waiter() throws SQLException {
+            c = openDefaultConnection();
+            ps = c.prepareStatement("insert into derby4711 values 1");
+        }
+
+        public void run() {
+            try {
+                runWaiter();
+            } catch (Throwable t) {
+                throwable = t;
+            }
+        };
+
+        private void runWaiter() throws SQLException {
+            long start = System.currentTimeMillis();
+            try {
+                ps.executeUpdate();
+            } finally {
+                ps.close();
+                c.close();
+            }
+            elapsedTime = System.currentTimeMillis() - start;
+        }
+    }
+}

Propchange: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/LockInterruptTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/_Suite.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/_Suite.java?rev=958264&r1=958263&r2=958264&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/_Suite.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/engine/_Suite.java Sat Jun 26 18:08:57 2010
@@ -47,6 +47,7 @@ public class _Suite extends BaseTestCase
         TestSuite suite = new TestSuite("engine");
 
         suite.addTest(ErrorStreamTest.suite());
+        suite.addTest(LockInterruptTest.suite());
         suite.addTest(ModuleLoadingTest.suite());
 
         return suite;

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseTestCase.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseTestCase.java?rev=958264&r1=958263&r2=958264&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseTestCase.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseTestCase.java Sat Jun 26 18:08:57 2010
@@ -622,15 +622,15 @@ public abstract class BaseTestCase
      * Fail; attaching an exception for more detail on cause.
      *
      * @param msg message explaining the failure
-     * @param e exception related to the cause
+     * @param t the cause of the failure
      *
      * @exception AssertionFailedError
      */
-    public static void fail(String msg, Exception e)
+    public static void fail(String msg, Throwable t)
             throws AssertionFailedError {
 
         AssertionFailedError ae = new AssertionFailedError(msg);
-        ae.initCause(e);
+        ae.initCause(t);
         throw ae;
     }
 } // End class BaseTestCase