提交 2b968a60 authored 作者: Andrei Tokar's avatar Andrei Tokar

covert MVStore.backgroundWriterThread into AtomicReference, eliminate race…

covert MVStore.backgroundWriterThread into AtomicReference, eliminate race condition in setAutoCommitDelay()
上级 45be4dac
...@@ -28,6 +28,7 @@ import java.util.concurrent.ThreadPoolExecutor; ...@@ -28,6 +28,7 @@ import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import org.h2.compress.CompressDeflate; import org.h2.compress.CompressDeflate;
import org.h2.compress.CompressLZF; import org.h2.compress.CompressLZF;
...@@ -178,9 +179,9 @@ public class MVStore implements AutoCloseable { ...@@ -178,9 +179,9 @@ public class MVStore implements AutoCloseable {
private final ReentrantLock storeLock = new ReentrantLock(true); private final ReentrantLock storeLock = new ReentrantLock(true);
/** /**
* The background thread, if any. * Reference to a background thread, which is expected to be running, if any.
*/ */
volatile BackgroundWriterThread backgroundWriterThread; private final AtomicReference<BackgroundWriterThread> backgroundWriterThread = new AtomicReference<>();
private volatile boolean reuseSpace = true; private volatile boolean reuseSpace = true;
...@@ -269,7 +270,8 @@ public class MVStore implements AutoCloseable { ...@@ -269,7 +270,8 @@ public class MVStore implements AutoCloseable {
private final AtomicLong oldestVersionToKeep = new AtomicLong(); private final AtomicLong oldestVersionToKeep = new AtomicLong();
/** /**
* Collection of all versions used by currently open transactions. * Ordered collection of all version usage counters for all versions starting
* from oldestVersionToKeep and up to current.
*/ */
private final Deque<TxCounter> versions = new LinkedList<>(); private final Deque<TxCounter> versions = new LinkedList<>();
...@@ -960,7 +962,7 @@ public class MVStore implements AutoCloseable { ...@@ -960,7 +962,7 @@ public class MVStore implements AutoCloseable {
while (!isClosed()) { while (!isClosed()) {
if (state.compareAndSet(STATE_OPEN, STATE_STOPPING)) { if (state.compareAndSet(STATE_OPEN, STATE_STOPPING)) {
try { try {
stopBackgroundThread(); stopBackgroundThread(normalShutdown);
storeLock.lock(); storeLock.lock();
try { try {
try { try {
...@@ -2777,7 +2779,7 @@ public class MVStore implements AutoCloseable { ...@@ -2777,7 +2779,7 @@ public class MVStore implements AutoCloseable {
private void handleException(Throwable ex) { private void handleException(Throwable ex) {
if (backgroundExceptionHandler != null) { if (backgroundExceptionHandler != null) {
try { try {
backgroundExceptionHandler.uncaughtException(null, ex); backgroundExceptionHandler.uncaughtException(Thread.currentThread(), ex);
} catch(Throwable ignore) { } catch(Throwable ignore) {
if (ex != ignore) { // OOME may be the same if (ex != ignore) { // OOME may be the same
ex.addSuppressed(ignore); ex.addSuppressed(ignore);
...@@ -2838,26 +2840,30 @@ public class MVStore implements AutoCloseable { ...@@ -2838,26 +2840,30 @@ public class MVStore implements AutoCloseable {
return state.get() <= STATE_STOPPING; return state.get() <= STATE_STOPPING;
} }
private void stopBackgroundThread() { private void stopBackgroundThread(boolean waitForIt) {
BackgroundWriterThread t = backgroundWriterThread; // Loop here is not strictly necessary, except for case of a spurious failure,
if (t == null) { // which should not happen with non-weak flavour of CAS operation,
return; // but I've seen it, so just to be safe...
} BackgroundWriterThread t;
backgroundWriterThread = null; while ((t = backgroundWriterThread.get()) != null &&
if (Thread.currentThread() == t) { // if called from within the thread itself - can not join
// within the thread itself - can not join t != Thread.currentThread()) {
return; if (backgroundWriterThread.compareAndSet(t, null)) {
}
synchronized (t.sync) { synchronized (t.sync) {
t.sync.notifyAll(); t.sync.notifyAll();
} }
if (waitForIt) {
try { try {
t.join(); t.join();
} catch (Exception e) { } catch (Exception e) {
// ignore // ignore
} }
} }
break;
}
}
}
/** /**
* Set the maximum delay in milliseconds to auto-commit changes. * Set the maximum delay in milliseconds to auto-commit changes.
...@@ -2878,18 +2884,23 @@ public class MVStore implements AutoCloseable { ...@@ -2878,18 +2884,23 @@ public class MVStore implements AutoCloseable {
if (fileStore == null || fileStore.isReadOnly()) { if (fileStore == null || fileStore.isReadOnly()) {
return; return;
} }
stopBackgroundThread(); stopBackgroundThread(true);
// start the background thread if needed // start the background thread if needed
if (millis > 0 && isOpen()) { 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,
fileStore.toString()); fileStore.toString());
if (backgroundWriterThread.compareAndSet(null, t)) {
t.start(); t.start();
backgroundWriterThread = t; }
} }
} }
boolean isBackgroundThread() {
return Thread.currentThread() == backgroundWriterThread.get();
}
/** /**
* Get the auto-commit delay. * Get the auto-commit delay.
* *
...@@ -3103,20 +3114,19 @@ public class MVStore implements AutoCloseable { ...@@ -3103,20 +3114,19 @@ public class MVStore implements AutoCloseable {
@Override @Override
public void run() { public void run() {
while (store.backgroundWriterThread != null) { while (store.isBackgroundThread()) {
synchronized (sync) { synchronized (sync) {
try { try {
sync.wait(sleep); sync.wait(sleep);
} catch (InterruptedException ignore) { } catch (InterruptedException ignore) {
} }
} }
if (store.backgroundWriterThread == null) { if (!store.isBackgroundThread()) {
break; break;
} }
store.writeInBackground(); store.writeInBackground();
} }
} }
} }
/** /**
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论