提交 45be4dac authored 作者: Andrei Tokar's avatar Andrei Tokar

convert MVStore.state into AtomicInteger, eliminate race on close()

上级 b55aaa2b
...@@ -148,23 +148,24 @@ public class MVStore implements AutoCloseable { ...@@ -148,23 +148,24 @@ public class MVStore implements AutoCloseable {
private static final int MARKED_FREE = 10_000_000; private static final int MARKED_FREE = 10_000_000;
/** /**
* Storage is open. * Store is open.
*/ */
private static final int STATE_OPEN = 0; private static final int STATE_OPEN = 0;
/** /**
* Storage is stopping now. Background writer thread finishes its work * Store is about to close now, but is still operational.
* during this process. * Outstanding store operation by background writer or other thread may be in progress.
* New updates must not be initiated, unless they are part of a closing procedure itself.
*/ */
private static final int STATE_STOPPING = 1; private static final int STATE_STOPPING = 1;
/** /**
* Storage is closing now. * Store is closing now, and any operation on it may fail.
*/ */
private static final int STATE_CLOSING = 2; private static final int STATE_CLOSING = 2;
/** /**
* Storage is closed. * Store is closed.
*/ */
private static final int STATE_CLOSED = 3; private static final int STATE_CLOSED = 3;
...@@ -183,7 +184,7 @@ public class MVStore implements AutoCloseable { ...@@ -183,7 +184,7 @@ public class MVStore implements AutoCloseable {
private volatile boolean reuseSpace = true; private volatile boolean reuseSpace = true;
private volatile int state; private final AtomicInteger state = new AtomicInteger();
private final FileStore fileStore; private final FileStore fileStore;
...@@ -435,7 +436,7 @@ public class MVStore implements AutoCloseable { ...@@ -435,7 +436,7 @@ public class MVStore implements AutoCloseable {
} }
private void panic(IllegalStateException e) { private void panic(IllegalStateException e) {
if (state == STATE_OPEN) { if (isOpen()) {
handleException(e); handleException(e);
panicException = e; panicException = e;
closeImmediately(); closeImmediately();
...@@ -939,27 +940,13 @@ public class MVStore implements AutoCloseable { ...@@ -939,27 +940,13 @@ public class MVStore implements AutoCloseable {
*/ */
@Override @Override
public void close() { public void close() {
if (isClosed()) {
return;
}
FileStore f = fileStore;
if (f != null && !f.isReadOnly()) {
stopBackgroundThread();
for (MVMap<?, ?> map : maps.values()) {
if (map.isClosed()) {
if (meta.remove(MVMap.getMapRootKey(map.getId())) != null) {
markMetaChanged();
}
}
}
commit();
}
closeStore(true); closeStore(true);
} }
/** /**
* Close the file and the store, without writing anything. This will stop * Close the file and the store, without writing anything.
* the background thread. This method ignores all errors. * This will try to stop the background thread (without waiting for it).
* This method ignores all errors.
*/ */
public void closeImmediately() { public void closeImmediately() {
try { try {
...@@ -969,43 +956,54 @@ public class MVStore implements AutoCloseable { ...@@ -969,43 +956,54 @@ public class MVStore implements AutoCloseable {
} }
} }
private void closeStore(boolean shrinkIfPossible) { private void closeStore(boolean normalShutdown) {
if (isClosed()) { while (!isClosed()) {
return; if (state.compareAndSet(STATE_OPEN, STATE_STOPPING)) {
}
state = STATE_STOPPING;
try {
stopBackgroundThread();
state = STATE_CLOSING;
storeLock.lock();
try {
try { try {
if (fileStore != null && shrinkIfPossible) { stopBackgroundThread();
shrinkFileIfPossible(0); storeLock.lock();
} try {
// release memory early - this is important when called try {
// because of out of memory if (normalShutdown && fileStore != null && !fileStore.isReadOnly()) {
if (cache != null) { for (MVMap<?, ?> map : maps.values()) {
cache.clear(); if (map.isClosed()) {
} if (meta.remove(MVMap.getMapRootKey(map.getId())) != null) {
if (cacheChunkRef != null) { markMetaChanged();
cacheChunkRef.clear(); }
} }
for (MVMap<?, ?> m : new ArrayList<>(maps.values())) { }
m.close(); commit();
shrinkFileIfPossible(0);
}
state.set(STATE_CLOSING);
// release memory early - this is important when called
// because of out of memory
if (cache != null) {
cache.clear();
}
if (cacheChunkRef != null) {
cacheChunkRef.clear();
}
for (MVMap<?, ?> m : new ArrayList<>(maps.values())) {
m.close();
}
chunks.clear();
maps.clear();
} finally {
if (fileStore != null && !fileStoreIsProvided) {
fileStore.close();
}
}
} finally {
storeLock.unlock();
} }
chunks.clear();
maps.clear();
} finally { } finally {
if (fileStore != null && !fileStoreIsProvided) { state.set(STATE_CLOSED);
fileStore.close();
}
} }
} finally {
storeLock.unlock();
} }
} finally {
state = STATE_CLOSED;
} }
} }
...@@ -2805,12 +2803,20 @@ public class MVStore implements AutoCloseable { ...@@ -2805,12 +2803,20 @@ public class MVStore implements AutoCloseable {
} }
} }
private boolean isOpen() {
return state.get() == STATE_OPEN;
}
/**
* Determine that store is open, or wait for it to be closed (by other thread)
* @return true if store is open, false otherwise
*/
public boolean isClosed() { public boolean isClosed() {
if (state == STATE_OPEN) { if (isOpen()) {
return false; return false;
} }
int millis = 1; int millis = 1;
while (state != STATE_CLOSED) { while (state.get() != STATE_CLOSED) {
/* /*
* We need to wait for completion of close procedure. This is * We need to wait for completion of close procedure. This is
* required because otherwise database may be closed too early while * required because otherwise database may be closed too early while
...@@ -2829,7 +2835,7 @@ public class MVStore implements AutoCloseable { ...@@ -2829,7 +2835,7 @@ public class MVStore implements AutoCloseable {
} }
private boolean isOpenOrStopping() { private boolean isOpenOrStopping() {
return state <= STATE_STOPPING; return state.get() <= STATE_STOPPING;
} }
private void stopBackgroundThread() { private void stopBackgroundThread() {
...@@ -2874,7 +2880,7 @@ public class MVStore implements AutoCloseable { ...@@ -2874,7 +2880,7 @@ public class MVStore implements AutoCloseable {
} }
stopBackgroundThread(); stopBackgroundThread();
// start the background thread if needed // start the background thread if needed
if (millis > 0 && state == STATE_OPEN) { if (millis > 0 && isOpen()) {
int sleep = Math.max(1, millis / 10); int sleep = Math.max(1, millis / 10);
BackgroundWriterThread t = BackgroundWriterThread t =
new BackgroundWriterThread(this, sleep, new BackgroundWriterThread(this, sleep,
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论