1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
|
/*
* 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 java.util.Properties;
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.SystemPropertyTestSetup;
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);
Properties syspros = new Properties();
//Derby-4856 interrupt error create thread dump and diagnostic
//info. Add property to avoid the information.
syspros.put("derby.stream.error.extendedDiagSeverityLevel", "50000");
test = new SystemPropertyTestSetup(test, syspros, true);
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 and give the interrupt a little time to do its work.
t1.interrupt();
Thread.sleep(1000);
// 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);
if (hasInterruptibleIO()) {
println("Skipping assert for t1.InterruptFlagSetOnThrow due " +
" to interruptible IO.");
println("This is default on Solaris/Sun Java <= 1.6, use " +
"-XX:-UseVMInterruptibleIO if available.");
// The flag will may get swallowed
} else {
assertTrue(t1.InterruptFlagSetOnThrow);
}
// 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 boolean InterruptFlagSetOnThrow;
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;
InterruptFlagSetOnThrow = interrupted(); // clears also
}
};
private void runWaiter() throws SQLException {
long start = System.currentTimeMillis();
try {
ps.executeUpdate();
} finally {
ps.close();
c.close();
}
elapsedTime = System.currentTimeMillis() - start;
}
}
}
|