Merge pull request #12599 from jpountz/fix/PrioritizedEsThreadPoolExecutor_concurrency

Fix concurrency issue in PrioritizedEsThreadPoolExecutor.
This commit is contained in:
Adrien Grand 2015-08-04 10:00:46 +02:00
commit caca13c878
1 changed files with 24 additions and 16 deletions

View File

@ -19,6 +19,7 @@
package org.elasticsearch.common.util.concurrent; package org.elasticsearch.common.util.concurrent;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import org.elasticsearch.common.Priority; import org.elasticsearch.common.Priority;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
@ -161,8 +162,10 @@ public class PrioritizedEsThreadPoolExecutor extends EsThreadPoolExecutor {
private Runnable runnable; private Runnable runnable;
private final long insertionOrder; private final long insertionOrder;
private volatile ScheduledFuture<?> timeoutFuture;
private volatile boolean started = false; // these two variables are protected by 'this'
private ScheduledFuture<?> timeoutFuture;
private boolean started = false;
TieBreakingPrioritizedRunnable(PrioritizedRunnable runnable, long insertionOrder) { TieBreakingPrioritizedRunnable(PrioritizedRunnable runnable, long insertionOrder) {
this(runnable, runnable.priority(), insertionOrder); this(runnable, runnable.priority(), insertionOrder);
@ -176,10 +179,12 @@ public class PrioritizedEsThreadPoolExecutor extends EsThreadPoolExecutor {
@Override @Override
public void run() { public void run() {
synchronized (this) {
// make the task as stared. This is needed for synchronization with the timeout handling // make the task as stared. This is needed for synchronization with the timeout handling
// see #scheduleTimeout() // see #scheduleTimeout()
started = true; started = true;
FutureUtils.cancel(timeoutFuture); FutureUtils.cancel(timeoutFuture);
}
runAndClean(runnable); runAndClean(runnable);
} }
@ -193,6 +198,11 @@ public class PrioritizedEsThreadPoolExecutor extends EsThreadPoolExecutor {
} }
public void scheduleTimeout(ScheduledExecutorService timer, final Runnable timeoutCallback, TimeValue timeValue) { public void scheduleTimeout(ScheduledExecutorService timer, final Runnable timeoutCallback, TimeValue timeValue) {
synchronized (this) {
if (timeoutFuture != null) {
throw new IllegalStateException("scheduleTimeout may only be called once");
}
if (started == false) {
timeoutFuture = timer.schedule(new Runnable() { timeoutFuture = timer.schedule(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -201,9 +211,7 @@ public class PrioritizedEsThreadPoolExecutor extends EsThreadPoolExecutor {
} }
} }
}, timeValue.nanos(), TimeUnit.NANOSECONDS); }, timeValue.nanos(), TimeUnit.NANOSECONDS);
if (started) { }
// if the actual action already it might have missed the setting of the future. Clean it ourselves.
FutureUtils.cancel(timeoutFuture);
} }
} }