blob: 0589f9551d0f92a7f0a83f01d26ad5aecda86ec7 [file] [log] [blame]
/*
*
* Derby - Class org.apache.derbyTesting.functionTests.tests.store.Derby3980DeadlockTest
*
* 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.store;
import java.io.File;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedList;
import java.util.Properties;
import junit.framework.Test;
import org.apache.derbyTesting.junit.BaseJDBCTestCase;
import org.apache.derbyTesting.junit.BaseTestCase;
import org.apache.derbyTesting.junit.CleanDatabaseTestSetup;
import org.apache.derbyTesting.junit.DatabasePropertyTestSetup;
import org.apache.derbyTesting.junit.SecurityManagerSetup;
import org.apache.derbyTesting.junit.SystemPropertyTestSetup;
import org.apache.derbyTesting.junit.TestConfiguration;
/**
* Test to test two threads doing select then delete of a row
* using REPEATABLE_READ isolation level. We should get a
* deadlock (SQLState 40001);
*
*/
public class Derby3980DeadlockTest extends BaseJDBCTestCase {
private final int THREAD_COUNT = 2;
private final LinkedList<Throwable> listExceptions =
new LinkedList<Throwable>();
private final Object syncObject = new Object();
private int startedCount = 0;
private static final String fprefix = "javacore";
private static final String POLICY_FILE_NAME =
"org/apache/derbyTesting/functionTests/tests/store/Derby3980DeadlockTest.policy";
public Derby3980DeadlockTest(String name) {
super(name);
}
public void test3980Deadlock() {
Thread [] t = new Thread[THREAD_COUNT];
createThreads(t);
waitForThreads(t);
checkExceptions();
}
/**
* Check we have one deadlock exception.
*/
private void checkExceptions() {
//Due to timing, you might see ERROR 40XL1: A lock could not be obtained
//instead of ERROR 40001 (DERBY-3980)
for (Throwable t : listExceptions) {
if (t instanceof SQLException) {
assertSQLState("40001", (SQLException) t);
} else {
fail("Unexpected exception", t);
}
}
assertEquals("Expected 1 exception, got" + listExceptions.size(),
1,listExceptions.size());
}
/**
* Decorate a test with SecurityManagerSetup, clientServersuite, and
* SupportFilesSetup.
*
* @return the decorated test
*/
private static Test decorateTest() {
Test test = TestConfiguration.clientServerSuite(Derby3980DeadlockTest.class);
Properties diagProperties = new Properties();
diagProperties.setProperty("derby.stream.error.extendedDiagSeverityLevel", "30000");
diagProperties.setProperty("derby.infolog.append", "true");
test = new SystemPropertyTestSetup(test, diagProperties, true);
// Install a security manager using the initial policy file.
return new SecurityManagerSetup(test, POLICY_FILE_NAME);
}
private void waitForThreads(Thread[] t) {
for (int i = 0; i < THREAD_COUNT; i++)
{
try {
t[i].join();
} catch (InterruptedException e){
fail(
"FAIL - InterruptedException thrown waiting for the threads");
}
}
}
private void createThreads(Thread[] t) {
for (int i = 0; i < THREAD_COUNT; i++)
{
t[i] = new Thread(new Runnable() {
public void run() {threadWorker(); }
private void threadWorker() {
Connection threadConnection = null;
try {
synchronized (syncObject) {
/* If a connection hasn't been opened for this thread, open one */
if (threadConnection == null){
threadConnection = openDefaultConnection();
}
/* A new thread started, so we increment the counter */
startedCount++;
/* Wake all the threads to run the check below */
syncObject.notifyAll();
while (startedCount < THREAD_COUNT) {
syncObject.wait();
}
}
Statement stmt = threadConnection.createStatement();
threadConnection.setAutoCommit(false);
/* set isolation level to repeatable read */
threadConnection.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
ResultSet rs = stmt.executeQuery("select * from t where i = 456");
while (rs.next());
//stmt.executeUpdate("update t set i = 456 where i = 456")
stmt.executeUpdate("delete from t where i = 456");
threadConnection.commit();
} catch (Exception e) {
synchronized(syncObject){
listExceptions.add(e);
}
}
}},"Thread"+i);
t[i].start();
}
}
public static Test suite() {
Test suite = decorateTest();
return new CleanDatabaseTestSetup(
DatabasePropertyTestSetup.setLockTimeouts(suite, 5, 10)) {
/**
* Creates the table used in the test cases.
*
*/
protected void decorateSQL(Statement s) throws SQLException {
s.executeUpdate("CREATE TABLE T (I INT)");
s.executeUpdate("INSERT INTO T VALUES(456)");
}
};
}
/**
* Use tearDown to cleanup some of the diagnostic information files
*/
protected void tearDown() throws Exception {
String dsh = BaseTestCase.getSystemProperty("user.dir");
try {
File basedir = new File(dsh);
String[] list = BaseTestCase.getFilesWith(basedir, fprefix);
removeFiles(list);
} catch (IllegalArgumentException e) {
fail("open directory");
}
super.tearDown();
}
}