提交 7abf17d8 authored 作者: andrei's avatar andrei

Fixes h2database/h2database#467 Deadlock between Sequence.flush() and Database.close()

上级 237dda41
......@@ -1216,45 +1216,47 @@ public class Database implements DataHandler {
* @param fromShutdownHook true if this method is called from the shutdown
* hook
*/
synchronized void close(boolean fromShutdownHook) {
if (closing) {
return;
}
throwLastBackgroundException();
if (fileLockMethod == FileLock.LOCK_SERIALIZED &&
!reconnectChangePending) {
// another connection may have written something - don't write
try {
closeOpenFilesAndUnlock(false);
} catch (DbException e) {
// ignore
}
traceSystem.close();
Engine.getInstance().close(databaseName);
return;
}
closing = true;
stopServer();
if (userSessions.size() > 0) {
if (!fromShutdownHook) {
void close(boolean fromShutdownHook) {
synchronized (this) {
if (closing) {
return;
}
trace.info("closing {0} from shutdown hook", databaseName);
closeAllSessionsException(null);
}
trace.info("closing {0}", databaseName);
if (eventListener != null) {
// allow the event listener to connect to the database
closing = false;
DatabaseEventListener e = eventListener;
// set it to null, to make sure it's called only once
eventListener = null;
e.closingDatabase();
if (userSessions.size() > 0) {
// if a connection was opened, we can't close the database
throwLastBackgroundException();
if (fileLockMethod == FileLock.LOCK_SERIALIZED &&
!reconnectChangePending) {
// another connection may have written something - don't write
try {
closeOpenFilesAndUnlock(false);
} catch (DbException e) {
// ignore
}
traceSystem.close();
Engine.getInstance().close(databaseName);
return;
}
closing = true;
stopServer();
if (userSessions.size() > 0) {
if (!fromShutdownHook) {
return;
}
trace.info("closing {0} from shutdown hook", databaseName);
closeAllSessionsException(null);
}
trace.info("closing {0}", databaseName);
if (eventListener != null) {
// allow the event listener to connect to the database
closing = false;
DatabaseEventListener e = eventListener;
// set it to null, to make sure it's called only once
eventListener = null;
e.closingDatabase();
if (userSessions.size() > 0) {
// if a connection was opened, we can't close the database
return;
}
closing = true;
}
}
removeOrphanedLobs();
try {
......
......@@ -32,7 +32,7 @@ public class Sequence extends SchemaObjectBase {
private long maxValue;
private boolean cycle;
private boolean belongsToTable;
private Object flushSync = new Object();
private final Object flushSync = new Object();
private boolean writeWithMargin;
/**
......@@ -294,32 +294,30 @@ public class Sequence extends SchemaObjectBase {
// locked it) because it must be committed immediately, otherwise
// other threads can not access the sys table.
Session sysSession = database.getSystemSession();
synchronized (sysSession) {
synchronized (flushSync) {
flushInternal(sysSession);
}
synchronized (database.isMultiThreaded() ? sysSession : database) {
flushInternal(sysSession);
sysSession.commit(false);
}
} else {
synchronized (session) {
synchronized (flushSync) {
flushInternal(session);
}
synchronized (database.isMultiThreaded() ? session : database) {
flushInternal(session);
}
}
}
private void flushInternal(Session session) {
final boolean metaWasLocked = database.lockMeta(session);
// just for this case, use the value with the margin
try {
writeWithMargin = true;
database.updateMeta(session, this);
} finally {
writeWithMargin = false;
}
if (!metaWasLocked) {
database.unlockMeta(session);
synchronized (flushSync) {
final boolean metaWasLocked = database.lockMeta(session);
// just for this case, use the value with the margin
try {
writeWithMargin = true;
database.updateMeta(session, this);
} finally {
writeWithMargin = false;
}
if (!metaWasLocked) {
database.unlockMeta(session);
}
}
}
......
......@@ -26,11 +26,6 @@ public class TestKillRestart extends TestBase {
if (config.networked) {
return;
}
if (config.fast) {
// using fast as a proxy for "running on Jenkins CI" where this test
// gets stuck waiting for the sub-process after killing it
return;
}
if (getBaseDir().indexOf(':') > 0) {
return;
}
......
......@@ -39,11 +39,6 @@ public class TestKillRestartMulti extends TestBase {
if (config.networked) {
return;
}
if (config.fast) {
// using fast as a proxy for "running on Jenkins CI" where this test
// gets stuck waiting for the sub-process after killing it
return;
}
if (getBaseDir().indexOf(':') > 0) {
return;
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论