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