提交 b3b45da6 authored 作者: Thomas Mueller's avatar Thomas Mueller

Sometimes a StackOverflow occured when checking for deadlock.

上级 8cff8c47
......@@ -18,7 +18,9 @@ Change Log
<h1>Change Log</h1>
<h2>Next Version (unreleased)</h2>
<ul><li>The Shell tool does no longer truncate results with only one column, and displays
<ul><li>Sometimes a StackOverflow occured when checking for deadlock. See also
http://code.google.com/p/h2database/issues/detail?id=61
</li><li>The Shell tool does no longer truncate results with only one column, and displays
a message if data was truncated.
</li></ul>
......
......@@ -9,6 +9,7 @@ package org.h2.table;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import org.h2.command.Prepared;
import org.h2.constant.ErrorCode;
......@@ -884,16 +885,16 @@ public abstract class Table extends SchemaObjectBase {
/**
* Check if a deadlock occurred. This method is called recursively. There is
* a circle if the session to be tested for is the same as the originating
* session (the 'clash session'). In this case the method must return an
* a circle if the session to be tested has already being visited (i.e., it
* is one of the 'clash sessions'). In this case the method must return an
* empty object array. Once a deadlock has been detected, the methods must
* add the session to the list.
*
* @param session the session to be tested for
* @param clash the originating session, and null when starting verification
* @param clash set with sessions already visited, and null when starting verification
* @return an object array with the sessions involved in the deadlock
*/
public ObjectArray checkDeadlock(Session session, Session clash) {
public ObjectArray checkDeadlock(Session session, Set clash) {
return null;
}
......
......@@ -10,6 +10,7 @@ import java.sql.SQLException;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.h2.api.DatabaseEventListener;
import org.h2.constant.ErrorCode;
......@@ -379,6 +380,7 @@ public class TableData extends Table implements RecordReader {
}
private void doLock(Session session, int lockMode, boolean exclusive) throws SQLException {
traceLock(session, exclusive, "requesting for");
long max = System.currentTimeMillis() + session.getLockTimeout();
boolean checkDeadlock = false;
while (true) {
......@@ -486,16 +488,17 @@ public class TableData extends Table implements RecordReader {
return buff.toString();
}
public ObjectArray checkDeadlock(Session session, Session clash) {
public ObjectArray checkDeadlock(Session session, Set clash) {
// only one deadlock check at any given time
synchronized (TableData.class) {
if (clash == null) {
// verification is started
clash = session;
} else if (clash == session) {
clash = new HashSet();
} else if (clash.contains(session)) {
// we found a circle
return new ObjectArray();
}
clash.add(session);
ObjectArray error = null;
for (Iterator it = lockShared.iterator(); it.hasNext();) {
Session s = (Session) it.next();
......
......@@ -48,6 +48,7 @@ public class TestDeadlock extends TestBase {
testLockUpgrade();
testThreePhilosophers();
testNoDeadlock();
testThreeSome();
deleteDb("deadlock");
}
......@@ -180,7 +181,7 @@ public class TestDeadlock extends TestBase {
}
t2.join();
t3.join();
checkDeadlock();
checkDeadlock(1);
c1.commit();
c2.commit();
c3.commit();
......@@ -188,6 +189,51 @@ public class TestDeadlock extends TestBase {
end();
}
// test case for issue # 61
// http://code.google.com/p/h2database/issues/detail?id=61)
private void testThreeSome() throws Exception {
if (config.mvcc) {
return;
}
initTest();
c1.createStatement().execute("CREATE TABLE TEST_A(ID INT PRIMARY KEY)");
c1.createStatement().execute("CREATE TABLE TEST_B(ID INT PRIMARY KEY)");
c1.createStatement().execute("CREATE TABLE TEST_C(ID INT PRIMARY KEY)");
c1.commit();
c1.createStatement().execute("INSERT INTO TEST_A VALUES(1)");
c1.createStatement().execute("INSERT INTO TEST_B VALUES(1)");
c2.createStatement().execute("INSERT INTO TEST_C VALUES(1)");
DoIt t2 = new DoIt() {
public void execute() throws SQLException {
c3.createStatement().execute("INSERT INTO TEST_B VALUES(2)");
c3.commit();
}
};
t2.start();
DoIt t3 = new DoIt() {
public void execute() throws SQLException {
c2.createStatement().execute("INSERT INTO TEST_A VALUES(2)");
c2.commit();
}
};
t3.start();
try {
c1.createStatement().execute("INSERT INTO TEST_C VALUES(2)");
c1.commit();
} catch (SQLException e) {
catchDeadlock(e);
c1.rollback();
}
t2.join();
t3.join();
checkDeadlock(2);
c1.commit();
c2.commit();
c3.commit();
c1.createStatement().execute("DROP TABLE TEST_A, TEST_B, TEST_C");
end();
}
private void testLockUpgrade() throws Exception {
if (config.mvcc) {
return;
......@@ -214,7 +260,7 @@ public class TestDeadlock extends TestBase {
catchDeadlock(e);
}
t1.join();
checkDeadlock();
checkDeadlock(1);
c1.commit();
c2.commit();
c1.createStatement().execute("DROP TABLE TEST");
......@@ -243,21 +289,23 @@ public class TestDeadlock extends TestBase {
catchDeadlock(e);
}
t1.join();
checkDeadlock();
checkDeadlock(1);
c1.commit();
c2.commit();
c1.createStatement().execute("DROP TABLE T1, T2");
end();
}
private void checkDeadlock() throws SQLException {
private void checkDeadlock(int max) throws SQLException {
assertTrue(lastException != null);
assertKnownException(lastException);
assertEquals(ErrorCode.DEADLOCK_1, lastException.getErrorCode());
SQLException e2 = lastException.getNextException();
if (e2 != null) {
if (e2 != null && max == 1) {
// we have two exception, but there should only be one
throw e2;
SQLException e3 = new SQLException("Expected one exception, got multiple");
e3.initCause(e2);
throw e3;
}
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论