6978087: jsr166y Updates
Simplify the ForkJoinPool API, reworking some of the internals Reviewed-by: martin, dholmes, chegar
This commit is contained in:
parent
4c7ea63262
commit
a4641686a0
File diff suppressed because it is too large
Load Diff
@ -91,10 +91,7 @@ import java.util.WeakHashMap;
|
||||
* results of a task is {@link #join}, but there are several variants:
|
||||
* The {@link Future#get} methods support interruptible and/or timed
|
||||
* waits for completion and report results using {@code Future}
|
||||
* conventions. Method {@link #helpJoin} enables callers to actively
|
||||
* execute other tasks while awaiting joins, which is sometimes more
|
||||
* efficient but only applies when all subtasks are known to be
|
||||
* strictly tree-structured. Method {@link #invoke} is semantically
|
||||
* conventions. Method {@link #invoke} is semantically
|
||||
* equivalent to {@code fork(); join()} but always attempts to begin
|
||||
* execution in the current thread. The "<em>quiet</em>" forms of
|
||||
* these methods do not extract results or report exceptions. These
|
||||
@ -130,7 +127,7 @@ import java.util.WeakHashMap;
|
||||
* ForkJoinTasks (as may be determined using method {@link
|
||||
* #inForkJoinPool}). Attempts to invoke them in other contexts
|
||||
* result in exceptions or errors, possibly including
|
||||
* ClassCastException.
|
||||
* {@code ClassCastException}.
|
||||
*
|
||||
* <p>Most base support methods are {@code final}, to prevent
|
||||
* overriding of implementations that are intrinsically tied to the
|
||||
@ -152,9 +149,8 @@ import java.util.WeakHashMap;
|
||||
*
|
||||
* <p>This class provides {@code adapt} methods for {@link Runnable}
|
||||
* and {@link Callable}, that may be of use when mixing execution of
|
||||
* {@code ForkJoinTasks} with other kinds of tasks. When all tasks
|
||||
* are of this form, consider using a pool in
|
||||
* {@linkplain ForkJoinPool#setAsyncMode async mode}.
|
||||
* {@code ForkJoinTasks} with other kinds of tasks. When all tasks are
|
||||
* of this form, consider using a pool constructed in <em>asyncMode</em>.
|
||||
*
|
||||
* <p>ForkJoinTasks are {@code Serializable}, which enables them to be
|
||||
* used in extensions such as remote execution frameworks. It is
|
||||
@ -166,33 +162,43 @@ import java.util.WeakHashMap;
|
||||
*/
|
||||
public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
|
||||
/**
|
||||
* Run control status bits packed into a single int to minimize
|
||||
* footprint and to ensure atomicity (via CAS). Status is
|
||||
* initially zero, and takes on nonnegative values until
|
||||
* completed, upon which status holds COMPLETED. CANCELLED, or
|
||||
* EXCEPTIONAL, which use the top 3 bits. Tasks undergoing
|
||||
* blocking waits by other threads have SIGNAL_MASK bits set --
|
||||
* bit 15 for external (nonFJ) waits, and the rest a count of
|
||||
* waiting FJ threads. (This representation relies on
|
||||
* ForkJoinPool max thread limits). Completion of a stolen task
|
||||
* with SIGNAL_MASK bits set awakens waiter via notifyAll. Even
|
||||
* though suboptimal for some purposes, we use basic builtin
|
||||
* wait/notify to take advantage of "monitor inflation" in JVMs
|
||||
* that we would otherwise need to emulate to avoid adding further
|
||||
* per-task bookkeeping overhead. Note that bits 16-28 are
|
||||
* currently unused. Also value 0x80000000 is available as spare
|
||||
* completion value.
|
||||
/*
|
||||
* See the internal documentation of class ForkJoinPool for a
|
||||
* general implementation overview. ForkJoinTasks are mainly
|
||||
* responsible for maintaining their "status" field amidst relays
|
||||
* to methods in ForkJoinWorkerThread and ForkJoinPool. The
|
||||
* methods of this class are more-or-less layered into (1) basic
|
||||
* status maintenance (2) execution and awaiting completion (3)
|
||||
* user-level methods that additionally report results. This is
|
||||
* sometimes hard to see because this file orders exported methods
|
||||
* in a way that flows well in javadocs. In particular, most
|
||||
* join mechanics are in method quietlyJoin, below.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The status field holds run control status bits packed into a
|
||||
* single int to minimize footprint and to ensure atomicity (via
|
||||
* CAS). Status is initially zero, and takes on nonnegative
|
||||
* values until completed, upon which status holds value
|
||||
* NORMAL, CANCELLED, or EXCEPTIONAL. Tasks undergoing blocking
|
||||
* waits by other threads have the SIGNAL bit set. Completion of
|
||||
* a stolen task with SIGNAL set awakens any waiters via
|
||||
* notifyAll. Even though suboptimal for some purposes, we use
|
||||
* basic builtin wait/notify to take advantage of "monitor
|
||||
* inflation" in JVMs that we would otherwise need to emulate to
|
||||
* avoid adding further per-task bookkeeping overhead. We want
|
||||
* these monitors to be "fat", i.e., not use biasing or thin-lock
|
||||
* techniques, so use some odd coding idioms that tend to avoid
|
||||
* them.
|
||||
*/
|
||||
|
||||
/** The run status of this task */
|
||||
volatile int status; // accessed directly by pool and workers
|
||||
|
||||
static final int COMPLETION_MASK = 0xe0000000;
|
||||
static final int NORMAL = 0xe0000000; // == mask
|
||||
static final int CANCELLED = 0xc0000000;
|
||||
static final int EXCEPTIONAL = 0xa0000000;
|
||||
static final int SIGNAL_MASK = 0x0000ffff;
|
||||
static final int INTERNAL_SIGNAL_MASK = 0x00007fff;
|
||||
static final int EXTERNAL_SIGNAL = 0x00008000; // top bit of low word
|
||||
private static final int NORMAL = -1;
|
||||
private static final int CANCELLED = -2;
|
||||
private static final int EXCEPTIONAL = -3;
|
||||
private static final int SIGNAL = 1;
|
||||
|
||||
/**
|
||||
* Table of exceptions thrown by tasks, to enable reporting by
|
||||
@ -206,176 +212,94 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
Collections.synchronizedMap
|
||||
(new WeakHashMap<ForkJoinTask<?>, Throwable>());
|
||||
|
||||
// within-package utilities
|
||||
// Maintaining completion status
|
||||
|
||||
/**
|
||||
* Gets current worker thread, or null if not a worker thread.
|
||||
*/
|
||||
static ForkJoinWorkerThread getWorker() {
|
||||
Thread t = Thread.currentThread();
|
||||
return ((t instanceof ForkJoinWorkerThread) ?
|
||||
(ForkJoinWorkerThread) t : null);
|
||||
}
|
||||
|
||||
final boolean casStatus(int cmp, int val) {
|
||||
return UNSAFE.compareAndSwapInt(this, statusOffset, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Workaround for not being able to rethrow unchecked exceptions.
|
||||
*/
|
||||
static void rethrowException(Throwable ex) {
|
||||
if (ex != null)
|
||||
UNSAFE.throwException(ex);
|
||||
}
|
||||
|
||||
// Setting completion status
|
||||
|
||||
/**
|
||||
* Marks completion and wakes up threads waiting to join this task.
|
||||
* Marks completion and wakes up threads waiting to join this task,
|
||||
* also clearing signal request bits.
|
||||
*
|
||||
* @param completion one of NORMAL, CANCELLED, EXCEPTIONAL
|
||||
*/
|
||||
final void setCompletion(int completion) {
|
||||
ForkJoinPool pool = getPool();
|
||||
if (pool != null) {
|
||||
int s; // Clear signal bits while setting completion status
|
||||
do {} while ((s = status) >= 0 && !casStatus(s, completion));
|
||||
|
||||
if ((s & SIGNAL_MASK) != 0) {
|
||||
if ((s &= INTERNAL_SIGNAL_MASK) != 0)
|
||||
pool.updateRunningCount(s);
|
||||
synchronized (this) { notifyAll(); }
|
||||
private void setCompletion(int completion) {
|
||||
int s;
|
||||
while ((s = status) >= 0) {
|
||||
if (UNSAFE.compareAndSwapInt(this, statusOffset, s, completion)) {
|
||||
if (s != 0)
|
||||
synchronized (this) { notifyAll(); }
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
externallySetCompletion(completion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Version of setCompletion for non-FJ threads. Leaves signal
|
||||
* bits for unblocked threads to adjust, and always notifies.
|
||||
* Records exception and sets exceptional completion.
|
||||
*
|
||||
* @return status on exit
|
||||
*/
|
||||
private void externallySetCompletion(int completion) {
|
||||
int s;
|
||||
do {} while ((s = status) >= 0 &&
|
||||
!casStatus(s, (s & SIGNAL_MASK) | completion));
|
||||
synchronized (this) { notifyAll(); }
|
||||
private void setExceptionalCompletion(Throwable rex) {
|
||||
exceptionMap.put(this, rex);
|
||||
setCompletion(EXCEPTIONAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets status to indicate normal completion.
|
||||
* Blocks a worker thread until completion. Called only by
|
||||
* pool. Currently unused -- pool-based waits use timeout
|
||||
* version below.
|
||||
*/
|
||||
final void setNormalCompletion() {
|
||||
// Try typical fast case -- single CAS, no signal, not already done.
|
||||
// Manually expand casStatus to improve chances of inlining it
|
||||
if (!UNSAFE.compareAndSwapInt(this, statusOffset, 0, NORMAL))
|
||||
setCompletion(NORMAL);
|
||||
}
|
||||
|
||||
// internal waiting and notification
|
||||
|
||||
/**
|
||||
* Performs the actual monitor wait for awaitDone.
|
||||
*/
|
||||
private void doAwaitDone() {
|
||||
// Minimize lock bias and in/de-flation effects by maximizing
|
||||
// chances of waiting inside sync
|
||||
try {
|
||||
while (status >= 0)
|
||||
synchronized (this) { if (status >= 0) wait(); }
|
||||
} catch (InterruptedException ie) {
|
||||
onInterruptedWait();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual timed monitor wait for awaitDone.
|
||||
*/
|
||||
private void doAwaitDone(long startTime, long nanos) {
|
||||
synchronized (this) {
|
||||
final void internalAwaitDone() {
|
||||
int s; // the odd construction reduces lock bias effects
|
||||
while ((s = status) >= 0) {
|
||||
try {
|
||||
while (status >= 0) {
|
||||
long nt = nanos - (System.nanoTime() - startTime);
|
||||
if (nt <= 0)
|
||||
break;
|
||||
wait(nt / 1000000, (int) (nt % 1000000));
|
||||
synchronized(this) {
|
||||
if (UNSAFE.compareAndSwapInt(this, statusOffset, s,SIGNAL))
|
||||
wait();
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
onInterruptedWait();
|
||||
cancelIfTerminating();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Awaiting completion
|
||||
|
||||
/**
|
||||
* Sets status to indicate there is joiner, then waits for join,
|
||||
* surrounded with pool notifications.
|
||||
* Blocks a worker thread until completed or timed out. Called
|
||||
* only by pool.
|
||||
*
|
||||
* @return status upon exit
|
||||
* @return status on exit
|
||||
*/
|
||||
private int awaitDone(ForkJoinWorkerThread w,
|
||||
boolean maintainParallelism) {
|
||||
ForkJoinPool pool = (w == null) ? null : w.pool;
|
||||
final int internalAwaitDone(long millis) {
|
||||
int s;
|
||||
while ((s = status) >= 0) {
|
||||
if (casStatus(s, (pool == null) ? s|EXTERNAL_SIGNAL : s+1)) {
|
||||
if (pool == null || !pool.preJoin(this, maintainParallelism))
|
||||
doAwaitDone();
|
||||
if (((s = status) & INTERNAL_SIGNAL_MASK) != 0)
|
||||
adjustPoolCountsOnUnblock(pool);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Timed version of awaitDone
|
||||
*
|
||||
* @return status upon exit
|
||||
*/
|
||||
private int awaitDone(ForkJoinWorkerThread w, long nanos) {
|
||||
ForkJoinPool pool = (w == null) ? null : w.pool;
|
||||
int s;
|
||||
while ((s = status) >= 0) {
|
||||
if (casStatus(s, (pool == null) ? s|EXTERNAL_SIGNAL : s+1)) {
|
||||
long startTime = System.nanoTime();
|
||||
if (pool == null || !pool.preJoin(this, false))
|
||||
doAwaitDone(startTime, nanos);
|
||||
if ((s = status) >= 0) {
|
||||
adjustPoolCountsOnCancelledWait(pool);
|
||||
s = status;
|
||||
if ((s = status) >= 0) {
|
||||
try {
|
||||
synchronized(this) {
|
||||
if (UNSAFE.compareAndSwapInt(this, statusOffset, s,SIGNAL))
|
||||
wait(millis, 0);
|
||||
}
|
||||
if (s < 0 && (s & INTERNAL_SIGNAL_MASK) != 0)
|
||||
adjustPoolCountsOnUnblock(pool);
|
||||
break;
|
||||
} catch (InterruptedException ie) {
|
||||
cancelIfTerminating();
|
||||
}
|
||||
s = status;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies pool that thread is unblocked. Called by signalled
|
||||
* threads when woken by non-FJ threads (which is atypical).
|
||||
* Blocks a non-worker-thread until completion.
|
||||
*/
|
||||
private void adjustPoolCountsOnUnblock(ForkJoinPool pool) {
|
||||
private void externalAwaitDone() {
|
||||
int s;
|
||||
do {} while ((s = status) < 0 && !casStatus(s, s & COMPLETION_MASK));
|
||||
if (pool != null && (s &= INTERNAL_SIGNAL_MASK) != 0)
|
||||
pool.updateRunningCount(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies pool to adjust counts on cancelled or timed out wait.
|
||||
*/
|
||||
private void adjustPoolCountsOnCancelledWait(ForkJoinPool pool) {
|
||||
if (pool != null) {
|
||||
int s;
|
||||
while ((s = status) >= 0 && (s & INTERNAL_SIGNAL_MASK) != 0) {
|
||||
if (casStatus(s, s - 1)) {
|
||||
pool.updateRunningCount(1);
|
||||
while ((s = status) >= 0) {
|
||||
synchronized(this) {
|
||||
if (UNSAFE.compareAndSwapInt(this, statusOffset, s, SIGNAL)){
|
||||
boolean interrupted = false;
|
||||
while (status >= 0) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException ie) {
|
||||
interrupted = true;
|
||||
}
|
||||
}
|
||||
if (interrupted)
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -383,153 +307,19 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles interruptions during waits.
|
||||
*/
|
||||
private void onInterruptedWait() {
|
||||
ForkJoinWorkerThread w = getWorker();
|
||||
if (w == null)
|
||||
Thread.currentThread().interrupt(); // re-interrupt
|
||||
else if (w.isTerminating())
|
||||
cancelIgnoringExceptions();
|
||||
// else if FJworker, ignore interrupt
|
||||
}
|
||||
|
||||
// Recording and reporting exceptions
|
||||
|
||||
private void setDoneExceptionally(Throwable rex) {
|
||||
exceptionMap.put(this, rex);
|
||||
setCompletion(EXCEPTIONAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws the exception associated with status s.
|
||||
*
|
||||
* @throws the exception
|
||||
*/
|
||||
private void reportException(int s) {
|
||||
if ((s &= COMPLETION_MASK) < NORMAL) {
|
||||
if (s == CANCELLED)
|
||||
throw new CancellationException();
|
||||
else
|
||||
rethrowException(exceptionMap.get(this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns result or throws exception using j.u.c.Future conventions.
|
||||
* Only call when {@code isDone} known to be true or thread known
|
||||
* to be interrupted.
|
||||
*/
|
||||
private V reportFutureResult()
|
||||
throws InterruptedException, ExecutionException {
|
||||
if (Thread.interrupted())
|
||||
throw new InterruptedException();
|
||||
int s = status & COMPLETION_MASK;
|
||||
if (s < NORMAL) {
|
||||
Throwable ex;
|
||||
if (s == CANCELLED)
|
||||
throw new CancellationException();
|
||||
if (s == EXCEPTIONAL && (ex = exceptionMap.get(this)) != null)
|
||||
throw new ExecutionException(ex);
|
||||
}
|
||||
return getRawResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns result or throws exception using j.u.c.Future conventions
|
||||
* with timeouts.
|
||||
*/
|
||||
private V reportTimedFutureResult()
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
if (Thread.interrupted())
|
||||
throw new InterruptedException();
|
||||
Throwable ex;
|
||||
int s = status & COMPLETION_MASK;
|
||||
if (s == NORMAL)
|
||||
return getRawResult();
|
||||
else if (s == CANCELLED)
|
||||
throw new CancellationException();
|
||||
else if (s == EXCEPTIONAL && (ex = exceptionMap.get(this)) != null)
|
||||
throw new ExecutionException(ex);
|
||||
else
|
||||
throw new TimeoutException();
|
||||
}
|
||||
|
||||
// internal execution methods
|
||||
|
||||
/**
|
||||
* Calls exec, recording completion, and rethrowing exception if
|
||||
* encountered. Caller should normally check status before calling.
|
||||
*
|
||||
* @return true if completed normally
|
||||
*/
|
||||
private boolean tryExec() {
|
||||
try { // try block must contain only call to exec
|
||||
if (!exec())
|
||||
return false;
|
||||
} catch (Throwable rex) {
|
||||
setDoneExceptionally(rex);
|
||||
rethrowException(rex);
|
||||
return false; // not reached
|
||||
}
|
||||
setNormalCompletion();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main execution method used by worker threads. Invokes
|
||||
* base computation unless already complete.
|
||||
* Unless done, calls exec and records status if completed, but
|
||||
* doesn't wait for completion otherwise. Primary execution method
|
||||
* for ForkJoinWorkerThread.
|
||||
*/
|
||||
final void quietlyExec() {
|
||||
if (status >= 0) {
|
||||
try {
|
||||
if (!exec())
|
||||
return;
|
||||
} catch (Throwable rex) {
|
||||
setDoneExceptionally(rex);
|
||||
try {
|
||||
if (status < 0 || !exec())
|
||||
return;
|
||||
}
|
||||
setNormalCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls exec(), recording but not rethrowing exception.
|
||||
* Caller should normally check status before calling.
|
||||
*
|
||||
* @return true if completed normally
|
||||
*/
|
||||
private boolean tryQuietlyInvoke() {
|
||||
try {
|
||||
if (!exec())
|
||||
return false;
|
||||
} catch (Throwable rex) {
|
||||
setDoneExceptionally(rex);
|
||||
return false;
|
||||
setExceptionalCompletion(rex);
|
||||
return;
|
||||
}
|
||||
setNormalCompletion();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels, ignoring any exceptions it throws.
|
||||
*/
|
||||
final void cancelIgnoringExceptions() {
|
||||
try {
|
||||
cancel(false);
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main implementation of helpJoin
|
||||
*/
|
||||
private int busyJoin(ForkJoinWorkerThread w) {
|
||||
int s;
|
||||
ForkJoinTask<?> t;
|
||||
while ((s = status) >= 0 && (t = w.scanWhileJoining(this)) != null)
|
||||
t.quietlyExec();
|
||||
return (s >= 0) ? awaitDone(w, false) : s; // block if no work
|
||||
setCompletion(NORMAL); // must be outside try block
|
||||
}
|
||||
|
||||
// public methods
|
||||
@ -567,34 +357,41 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
* @return the computed result
|
||||
*/
|
||||
public final V join() {
|
||||
ForkJoinWorkerThread w = getWorker();
|
||||
if (w == null || status < 0 || !w.unpushTask(this) || !tryExec())
|
||||
reportException(awaitDone(w, true));
|
||||
quietlyJoin();
|
||||
Throwable ex;
|
||||
if (status < NORMAL && (ex = getException()) != null)
|
||||
UNSAFE.throwException(ex);
|
||||
return getRawResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Commences performing this task, awaits its completion if
|
||||
* necessary, and return its result, or throws an (unchecked)
|
||||
* exception if the underlying computation did so.
|
||||
* necessary, and returns its result, or throws an (unchecked)
|
||||
* {@code RuntimeException} or {@code Error} if the underlying
|
||||
* computation did so.
|
||||
*
|
||||
* @return the computed result
|
||||
*/
|
||||
public final V invoke() {
|
||||
if (status >= 0 && tryExec())
|
||||
return getRawResult();
|
||||
else
|
||||
return join();
|
||||
quietlyInvoke();
|
||||
Throwable ex;
|
||||
if (status < NORMAL && (ex = getException()) != null)
|
||||
UNSAFE.throwException(ex);
|
||||
return getRawResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Forks the given tasks, returning when {@code isDone} holds for
|
||||
* each task or an (unchecked) exception is encountered, in which
|
||||
* case the exception is rethrown. If either task encounters an
|
||||
* exception, the other one may be, but is not guaranteed to be,
|
||||
* cancelled. If both tasks throw an exception, then this method
|
||||
* throws one of them. The individual status of each task may be
|
||||
* checked using {@link #getException()} and related methods.
|
||||
* case the exception is rethrown. If more than one task
|
||||
* encounters an exception, then this method throws any one of
|
||||
* these exceptions. If any task encounters an exception, the
|
||||
* other may be cancelled. However, the execution status of
|
||||
* individual tasks is not guaranteed upon exceptional return. The
|
||||
* status of each task may be obtained using {@link
|
||||
* #getException()} and related methods to check if they have been
|
||||
* cancelled, completed normally or exceptionally, or left
|
||||
* unprocessed.
|
||||
*
|
||||
* <p>This method may be invoked only from within {@code
|
||||
* ForkJoinTask} computations (as may be determined using method
|
||||
@ -615,12 +412,14 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
/**
|
||||
* Forks the given tasks, returning when {@code isDone} holds for
|
||||
* each task or an (unchecked) exception is encountered, in which
|
||||
* case the exception is rethrown. If any task encounters an
|
||||
* exception, others may be, but are not guaranteed to be,
|
||||
* cancelled. If more than one task encounters an exception, then
|
||||
* this method throws any one of these exceptions. The individual
|
||||
* status of each task may be checked using {@link #getException()}
|
||||
* and related methods.
|
||||
* case the exception is rethrown. If more than one task
|
||||
* encounters an exception, then this method throws any one of
|
||||
* these exceptions. If any task encounters an exception, others
|
||||
* may be cancelled. However, the execution status of individual
|
||||
* tasks is not guaranteed upon exceptional return. The status of
|
||||
* each task may be obtained using {@link #getException()} and
|
||||
* related methods to check if they have been cancelled, completed
|
||||
* normally or exceptionally, or left unprocessed.
|
||||
*
|
||||
* <p>This method may be invoked only from within {@code
|
||||
* ForkJoinTask} computations (as may be determined using method
|
||||
@ -644,7 +443,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
t.fork();
|
||||
else {
|
||||
t.quietlyInvoke();
|
||||
if (ex == null)
|
||||
if (ex == null && t.status < NORMAL)
|
||||
ex = t.getException();
|
||||
}
|
||||
}
|
||||
@ -655,26 +454,27 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
t.cancel(false);
|
||||
else {
|
||||
t.quietlyJoin();
|
||||
if (ex == null)
|
||||
if (ex == null && t.status < NORMAL)
|
||||
ex = t.getException();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ex != null)
|
||||
rethrowException(ex);
|
||||
UNSAFE.throwException(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forks all tasks in the specified collection, returning when
|
||||
* {@code isDone} holds for each task or an (unchecked) exception
|
||||
* is encountered. If any task encounters an exception, others
|
||||
* may be, but are not guaranteed to be, cancelled. If more than
|
||||
* one task encounters an exception, then this method throws any
|
||||
* one of these exceptions. The individual status of each task
|
||||
* may be checked using {@link #getException()} and related
|
||||
* methods. The behavior of this operation is undefined if the
|
||||
* specified collection is modified while the operation is in
|
||||
* progress.
|
||||
* is encountered, in which case the exception is rethrown. If
|
||||
* more than one task encounters an exception, then this method
|
||||
* throws any one of these exceptions. If any task encounters an
|
||||
* exception, others may be cancelled. However, the execution
|
||||
* status of individual tasks is not guaranteed upon exceptional
|
||||
* return. The status of each task may be obtained using {@link
|
||||
* #getException()} and related methods to check if they have been
|
||||
* cancelled, completed normally or exceptionally, or left
|
||||
* unprocessed.
|
||||
*
|
||||
* <p>This method may be invoked only from within {@code
|
||||
* ForkJoinTask} computations (as may be determined using method
|
||||
@ -706,7 +506,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
t.fork();
|
||||
else {
|
||||
t.quietlyInvoke();
|
||||
if (ex == null)
|
||||
if (ex == null && t.status < NORMAL)
|
||||
ex = t.getException();
|
||||
}
|
||||
}
|
||||
@ -717,13 +517,13 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
t.cancel(false);
|
||||
else {
|
||||
t.quietlyJoin();
|
||||
if (ex == null)
|
||||
if (ex == null && t.status < NORMAL)
|
||||
ex = t.getException();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ex != null)
|
||||
rethrowException(ex);
|
||||
UNSAFE.throwException(ex);
|
||||
return tasks;
|
||||
}
|
||||
|
||||
@ -753,7 +553,35 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
*/
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
setCompletion(CANCELLED);
|
||||
return (status & COMPLETION_MASK) == CANCELLED;
|
||||
return status == CANCELLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels, ignoring any exceptions thrown by cancel. Used during
|
||||
* worker and pool shutdown. Cancel is spec'ed not to throw any
|
||||
* exceptions, but if it does anyway, we have no recourse during
|
||||
* shutdown, so guard against this case.
|
||||
*/
|
||||
final void cancelIgnoringExceptions() {
|
||||
try {
|
||||
cancel(false);
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels if current thread is a terminating worker thread,
|
||||
* ignoring any exceptions thrown by cancel.
|
||||
*/
|
||||
final void cancelIfTerminating() {
|
||||
Thread t = Thread.currentThread();
|
||||
if ((t instanceof ForkJoinWorkerThread) &&
|
||||
((ForkJoinWorkerThread) t).isTerminating()) {
|
||||
try {
|
||||
cancel(false);
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean isDone() {
|
||||
@ -761,7 +589,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
}
|
||||
|
||||
public final boolean isCancelled() {
|
||||
return (status & COMPLETION_MASK) == CANCELLED;
|
||||
return status == CANCELLED;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -770,7 +598,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
* @return {@code true} if this task threw an exception or was cancelled
|
||||
*/
|
||||
public final boolean isCompletedAbnormally() {
|
||||
return (status & COMPLETION_MASK) < NORMAL;
|
||||
return status < NORMAL;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -781,7 +609,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
* exception and was not cancelled
|
||||
*/
|
||||
public final boolean isCompletedNormally() {
|
||||
return (status & COMPLETION_MASK) == NORMAL;
|
||||
return status == NORMAL;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -792,7 +620,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
* @return the exception, or {@code null} if none
|
||||
*/
|
||||
public final Throwable getException() {
|
||||
int s = status & COMPLETION_MASK;
|
||||
int s = status;
|
||||
return ((s >= NORMAL) ? null :
|
||||
(s == CANCELLED) ? new CancellationException() :
|
||||
exceptionMap.get(this));
|
||||
@ -813,20 +641,21 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
* thrown will be a {@code RuntimeException} with cause {@code ex}.
|
||||
*/
|
||||
public void completeExceptionally(Throwable ex) {
|
||||
setDoneExceptionally((ex instanceof RuntimeException) ||
|
||||
(ex instanceof Error) ? ex :
|
||||
new RuntimeException(ex));
|
||||
setExceptionalCompletion((ex instanceof RuntimeException) ||
|
||||
(ex instanceof Error) ? ex :
|
||||
new RuntimeException(ex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes this task, and if not already aborted or cancelled,
|
||||
* returning a {@code null} result upon {@code join} and related
|
||||
* operations. This method may be used to provide results for
|
||||
* asynchronous tasks, or to provide alternative handling for
|
||||
* tasks that would not otherwise complete normally. Its use in
|
||||
* other situations is discouraged. This method is
|
||||
* overridable, but overridden versions must invoke {@code super}
|
||||
* implementation to maintain guarantees.
|
||||
* returning the given value as the result of subsequent
|
||||
* invocations of {@code join} and related operations. This method
|
||||
* may be used to provide results for asynchronous tasks, or to
|
||||
* provide alternative handling for tasks that would not otherwise
|
||||
* complete normally. Its use in other situations is
|
||||
* discouraged. This method is overridable, but overridden
|
||||
* versions must invoke {@code super} implementation to maintain
|
||||
* guarantees.
|
||||
*
|
||||
* @param value the result value for this task
|
||||
*/
|
||||
@ -834,97 +663,151 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
try {
|
||||
setRawResult(value);
|
||||
} catch (Throwable rex) {
|
||||
setDoneExceptionally(rex);
|
||||
setExceptionalCompletion(rex);
|
||||
return;
|
||||
}
|
||||
setNormalCompletion();
|
||||
setCompletion(NORMAL);
|
||||
}
|
||||
|
||||
public final V get() throws InterruptedException, ExecutionException {
|
||||
ForkJoinWorkerThread w = getWorker();
|
||||
if (w == null || status < 0 || !w.unpushTask(this) || !tryQuietlyInvoke())
|
||||
awaitDone(w, true);
|
||||
return reportFutureResult();
|
||||
quietlyJoin();
|
||||
if (Thread.interrupted())
|
||||
throw new InterruptedException();
|
||||
int s = status;
|
||||
if (s < NORMAL) {
|
||||
Throwable ex;
|
||||
if (s == CANCELLED)
|
||||
throw new CancellationException();
|
||||
if (s == EXCEPTIONAL && (ex = exceptionMap.get(this)) != null)
|
||||
throw new ExecutionException(ex);
|
||||
}
|
||||
return getRawResult();
|
||||
}
|
||||
|
||||
public final V get(long timeout, TimeUnit unit)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
Thread t = Thread.currentThread();
|
||||
ForkJoinPool pool;
|
||||
if (t instanceof ForkJoinWorkerThread) {
|
||||
ForkJoinWorkerThread w = (ForkJoinWorkerThread) t;
|
||||
if (status >= 0 && w.unpushTask(this))
|
||||
quietlyExec();
|
||||
pool = w.pool;
|
||||
}
|
||||
else
|
||||
pool = null;
|
||||
/*
|
||||
* Timed wait loop intermixes cases for FJ (pool != null) and
|
||||
* non FJ threads. For FJ, decrement pool count but don't try
|
||||
* for replacement; increment count on completion. For non-FJ,
|
||||
* deal with interrupts. This is messy, but a little less so
|
||||
* than is splitting the FJ and nonFJ cases.
|
||||
*/
|
||||
boolean interrupted = false;
|
||||
boolean dec = false; // true if pool count decremented
|
||||
long nanos = unit.toNanos(timeout);
|
||||
ForkJoinWorkerThread w = getWorker();
|
||||
if (w == null || status < 0 || !w.unpushTask(this) || !tryQuietlyInvoke())
|
||||
awaitDone(w, nanos);
|
||||
return reportTimedFutureResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly executes other tasks until this task {@link #isDone is
|
||||
* done}, then returns the result of the computation. This method
|
||||
* may be more efficient than {@code join}, but is only applicable
|
||||
* when there are no potential dependencies between continuation
|
||||
* of the current task and that of any other task that might be
|
||||
* executed while helping. (This usually holds for pure
|
||||
* divide-and-conquer tasks).
|
||||
*
|
||||
* <p>This method may be invoked only from within {@code
|
||||
* ForkJoinTask} computations (as may be determined using method
|
||||
* {@link #inForkJoinPool}). Attempts to invoke in other contexts
|
||||
* result in exceptions or errors, possibly including {@code
|
||||
* ClassCastException}.
|
||||
*
|
||||
* @return the computed result
|
||||
*/
|
||||
public final V helpJoin() {
|
||||
ForkJoinWorkerThread w = (ForkJoinWorkerThread) Thread.currentThread();
|
||||
if (status < 0 || !w.unpushTask(this) || !tryExec())
|
||||
reportException(busyJoin(w));
|
||||
for (;;) {
|
||||
if (pool == null && Thread.interrupted()) {
|
||||
interrupted = true;
|
||||
break;
|
||||
}
|
||||
int s = status;
|
||||
if (s < 0)
|
||||
break;
|
||||
if (UNSAFE.compareAndSwapInt(this, statusOffset, s, SIGNAL)) {
|
||||
long startTime = System.nanoTime();
|
||||
long nt; // wait time
|
||||
while (status >= 0 &&
|
||||
(nt = nanos - (System.nanoTime() - startTime)) > 0) {
|
||||
if (pool != null && !dec)
|
||||
dec = pool.tryDecrementRunningCount();
|
||||
else {
|
||||
long ms = nt / 1000000;
|
||||
int ns = (int) (nt % 1000000);
|
||||
try {
|
||||
synchronized(this) {
|
||||
if (status >= 0)
|
||||
wait(ms, ns);
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
if (pool != null)
|
||||
cancelIfTerminating();
|
||||
else {
|
||||
interrupted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pool != null && dec)
|
||||
pool.incrementRunningCount();
|
||||
if (interrupted)
|
||||
throw new InterruptedException();
|
||||
int es = status;
|
||||
if (es != NORMAL) {
|
||||
Throwable ex;
|
||||
if (es == CANCELLED)
|
||||
throw new CancellationException();
|
||||
if (es == EXCEPTIONAL && (ex = exceptionMap.get(this)) != null)
|
||||
throw new ExecutionException(ex);
|
||||
throw new TimeoutException();
|
||||
}
|
||||
return getRawResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibly executes other tasks until this task {@link #isDone is
|
||||
* done}. This method may be useful when processing collections
|
||||
* of tasks when some have been cancelled or otherwise known to
|
||||
* have aborted.
|
||||
*
|
||||
* <p>This method may be invoked only from within {@code
|
||||
* ForkJoinTask} computations (as may be determined using method
|
||||
* {@link #inForkJoinPool}). Attempts to invoke in other contexts
|
||||
* result in exceptions or errors, possibly including {@code
|
||||
* ClassCastException}.
|
||||
*/
|
||||
public final void quietlyHelpJoin() {
|
||||
if (status >= 0) {
|
||||
ForkJoinWorkerThread w =
|
||||
(ForkJoinWorkerThread) Thread.currentThread();
|
||||
if (!w.unpushTask(this) || !tryQuietlyInvoke())
|
||||
busyJoin(w);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins this task, without returning its result or throwing an
|
||||
* Joins this task, without returning its result or throwing its
|
||||
* exception. This method may be useful when processing
|
||||
* collections of tasks when some have been cancelled or otherwise
|
||||
* known to have aborted.
|
||||
*/
|
||||
public final void quietlyJoin() {
|
||||
if (status >= 0) {
|
||||
ForkJoinWorkerThread w = getWorker();
|
||||
if (w == null || !w.unpushTask(this) || !tryQuietlyInvoke())
|
||||
awaitDone(w, true);
|
||||
Thread t;
|
||||
if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) {
|
||||
ForkJoinWorkerThread w = (ForkJoinWorkerThread) t;
|
||||
if (status >= 0) {
|
||||
if (w.unpushTask(this)) {
|
||||
boolean completed;
|
||||
try {
|
||||
completed = exec();
|
||||
} catch (Throwable rex) {
|
||||
setExceptionalCompletion(rex);
|
||||
return;
|
||||
}
|
||||
if (completed) {
|
||||
setCompletion(NORMAL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
w.joinTask(this);
|
||||
}
|
||||
}
|
||||
else
|
||||
externalAwaitDone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Commences performing this task and awaits its completion if
|
||||
* necessary, without returning its result or throwing an
|
||||
* exception. This method may be useful when processing
|
||||
* collections of tasks when some have been cancelled or otherwise
|
||||
* known to have aborted.
|
||||
* necessary, without returning its result or throwing its
|
||||
* exception.
|
||||
*/
|
||||
public final void quietlyInvoke() {
|
||||
if (status >= 0 && !tryQuietlyInvoke())
|
||||
quietlyJoin();
|
||||
if (status >= 0) {
|
||||
boolean completed;
|
||||
try {
|
||||
completed = exec();
|
||||
} catch (Throwable rex) {
|
||||
setExceptionalCompletion(rex);
|
||||
return;
|
||||
}
|
||||
if (completed)
|
||||
setCompletion(NORMAL);
|
||||
else
|
||||
quietlyJoin();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -956,7 +839,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
* pre-constructed trees of subtasks in loops.
|
||||
*/
|
||||
public void reinitialize() {
|
||||
if ((status & COMPLETION_MASK) == EXCEPTIONAL)
|
||||
if (status == EXCEPTIONAL)
|
||||
exceptionMap.remove(this);
|
||||
status = 0;
|
||||
}
|
||||
@ -1246,7 +1129,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
private static final long serialVersionUID = -7721805057305804111L;
|
||||
|
||||
/**
|
||||
* Saves the state to a stream.
|
||||
* Saves the state to a stream (that is, serializes it).
|
||||
*
|
||||
* @serialData the current run status and the exception thrown
|
||||
* during execution, or {@code null} if none
|
||||
@ -1259,18 +1142,16 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconstitutes the instance from a stream.
|
||||
* Reconstitutes the instance from a stream (that is, deserializes it).
|
||||
*
|
||||
* @param s the stream
|
||||
*/
|
||||
private void readObject(java.io.ObjectInputStream s)
|
||||
throws java.io.IOException, ClassNotFoundException {
|
||||
s.defaultReadObject();
|
||||
status &= ~INTERNAL_SIGNAL_MASK; // clear internal signal counts
|
||||
status |= EXTERNAL_SIGNAL; // conservatively set external signal
|
||||
Object ex = s.readObject();
|
||||
if (ex != null)
|
||||
setDoneExceptionally((Throwable) ex);
|
||||
setExceptionalCompletion((Throwable) ex);
|
||||
}
|
||||
|
||||
// Unsafe mechanics
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -42,6 +42,7 @@ import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
/**
|
||||
* An unbounded {@link TransferQueue} based on linked nodes.
|
||||
* This queue orders elements FIFO (first-in-first-out) with respect
|
||||
@ -233,24 +234,6 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
* additional GC bookkeeping ("write barriers") that are sometimes
|
||||
* more costly than the writes themselves because of contention).
|
||||
*
|
||||
* Removal of interior nodes (due to timed out or interrupted
|
||||
* waits, or calls to remove(x) or Iterator.remove) can use a
|
||||
* scheme roughly similar to that described in Scherer, Lea, and
|
||||
* Scott's SynchronousQueue. Given a predecessor, we can unsplice
|
||||
* any node except the (actual) tail of the queue. To avoid
|
||||
* build-up of cancelled trailing nodes, upon a request to remove
|
||||
* a trailing node, it is placed in field "cleanMe" to be
|
||||
* unspliced upon the next call to unsplice any other node.
|
||||
* Situations needing such mechanics are not common but do occur
|
||||
* in practice; for example when an unbounded series of short
|
||||
* timed calls to poll repeatedly time out but never otherwise
|
||||
* fall off the list because of an untimed call to take at the
|
||||
* front of the queue. Note that maintaining field cleanMe does
|
||||
* not otherwise much impact garbage retention even if never
|
||||
* cleared by some other call because the held node will
|
||||
* eventually either directly or indirectly lead to a self-link
|
||||
* once off the list.
|
||||
*
|
||||
* *** Overview of implementation ***
|
||||
*
|
||||
* We use a threshold-based approach to updates, with a slack
|
||||
@ -266,15 +249,10 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
* per-thread one available, but even ThreadLocalRandom is too
|
||||
* heavy for these purposes.
|
||||
*
|
||||
* With such a small slack threshold value, it is rarely
|
||||
* worthwhile to augment this with path short-circuiting; i.e.,
|
||||
* unsplicing nodes between head and the first unmatched node, or
|
||||
* similarly for tail, rather than advancing head or tail
|
||||
* proper. However, it is used (in awaitMatch) immediately before
|
||||
* a waiting thread starts to block, as a final bit of helping at
|
||||
* a point when contention with others is extremely unlikely
|
||||
* (since if other threads that could release it are operating,
|
||||
* then the current thread wouldn't be blocking).
|
||||
* With such a small slack threshold value, it is not worthwhile
|
||||
* to augment this with path short-circuiting (i.e., unsplicing
|
||||
* interior nodes) except in the case of cancellation/removal (see
|
||||
* below).
|
||||
*
|
||||
* We allow both the head and tail fields to be null before any
|
||||
* nodes are enqueued; initializing upon first append. This
|
||||
@ -356,6 +334,70 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
* versa) compared to their predecessors receive additional
|
||||
* chained spins, reflecting longer paths typically required to
|
||||
* unblock threads during phase changes.
|
||||
*
|
||||
*
|
||||
* ** Unlinking removed interior nodes **
|
||||
*
|
||||
* In addition to minimizing garbage retention via self-linking
|
||||
* described above, we also unlink removed interior nodes. These
|
||||
* may arise due to timed out or interrupted waits, or calls to
|
||||
* remove(x) or Iterator.remove. Normally, given a node that was
|
||||
* at one time known to be the predecessor of some node s that is
|
||||
* to be removed, we can unsplice s by CASing the next field of
|
||||
* its predecessor if it still points to s (otherwise s must
|
||||
* already have been removed or is now offlist). But there are two
|
||||
* situations in which we cannot guarantee to make node s
|
||||
* unreachable in this way: (1) If s is the trailing node of list
|
||||
* (i.e., with null next), then it is pinned as the target node
|
||||
* for appends, so can only be removed later after other nodes are
|
||||
* appended. (2) We cannot necessarily unlink s given a
|
||||
* predecessor node that is matched (including the case of being
|
||||
* cancelled): the predecessor may already be unspliced, in which
|
||||
* case some previous reachable node may still point to s.
|
||||
* (For further explanation see Herlihy & Shavit "The Art of
|
||||
* Multiprocessor Programming" chapter 9). Although, in both
|
||||
* cases, we can rule out the need for further action if either s
|
||||
* or its predecessor are (or can be made to be) at, or fall off
|
||||
* from, the head of list.
|
||||
*
|
||||
* Without taking these into account, it would be possible for an
|
||||
* unbounded number of supposedly removed nodes to remain
|
||||
* reachable. Situations leading to such buildup are uncommon but
|
||||
* can occur in practice; for example when a series of short timed
|
||||
* calls to poll repeatedly time out but never otherwise fall off
|
||||
* the list because of an untimed call to take at the front of the
|
||||
* queue.
|
||||
*
|
||||
* When these cases arise, rather than always retraversing the
|
||||
* entire list to find an actual predecessor to unlink (which
|
||||
* won't help for case (1) anyway), we record a conservative
|
||||
* estimate of possible unsplice failures (in "sweepVotes").
|
||||
* We trigger a full sweep when the estimate exceeds a threshold
|
||||
* ("SWEEP_THRESHOLD") indicating the maximum number of estimated
|
||||
* removal failures to tolerate before sweeping through, unlinking
|
||||
* cancelled nodes that were not unlinked upon initial removal.
|
||||
* We perform sweeps by the thread hitting threshold (rather than
|
||||
* background threads or by spreading work to other threads)
|
||||
* because in the main contexts in which removal occurs, the
|
||||
* caller is already timed-out, cancelled, or performing a
|
||||
* potentially O(n) operation (e.g. remove(x)), none of which are
|
||||
* time-critical enough to warrant the overhead that alternatives
|
||||
* would impose on other threads.
|
||||
*
|
||||
* Because the sweepVotes estimate is conservative, and because
|
||||
* nodes become unlinked "naturally" as they fall off the head of
|
||||
* the queue, and because we allow votes to accumulate even while
|
||||
* sweeps are in progress, there are typically significantly fewer
|
||||
* such nodes than estimated. Choice of a threshold value
|
||||
* balances the likelihood of wasted effort and contention, versus
|
||||
* providing a worst-case bound on retention of interior nodes in
|
||||
* quiescent queues. The value defined below was chosen
|
||||
* empirically to balance these under various timeout scenarios.
|
||||
*
|
||||
* Note that we cannot self-link unlinked interior nodes during
|
||||
* sweeps. However, the associated garbage chains terminate when
|
||||
* some successor ultimately falls off the head of the list and is
|
||||
* self-linked.
|
||||
*/
|
||||
|
||||
/** True if on multiprocessor */
|
||||
@ -381,12 +423,20 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
*/
|
||||
private static final int CHAINED_SPINS = FRONT_SPINS >>> 1;
|
||||
|
||||
/**
|
||||
* The maximum number of estimated removal failures (sweepVotes)
|
||||
* to tolerate before sweeping through the queue unlinking
|
||||
* cancelled nodes that were not unlinked upon initial
|
||||
* removal. See above for explanation. The value must be at least
|
||||
* two to avoid useless sweeps when removing trailing nodes.
|
||||
*/
|
||||
static final int SWEEP_THRESHOLD = 32;
|
||||
|
||||
/**
|
||||
* Queue nodes. Uses Object, not E, for items to allow forgetting
|
||||
* them after use. Relies heavily on Unsafe mechanics to minimize
|
||||
* unnecessary ordering constraints: Writes that intrinsically
|
||||
* precede or follow CASes use simple relaxed forms. Other
|
||||
* cleanups use releasing/lazy writes.
|
||||
* unnecessary ordering constraints: Writes that are intrinsically
|
||||
* ordered wrt other accesses or CASes use simple relaxed forms.
|
||||
*/
|
||||
static final class Node {
|
||||
final boolean isData; // false if this is a request node
|
||||
@ -400,13 +450,13 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
}
|
||||
|
||||
final boolean casItem(Object cmp, Object val) {
|
||||
// assert cmp == null || cmp.getClass() != Node.class;
|
||||
// assert cmp == null || cmp.getClass() != Node.class;
|
||||
return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new node. Uses relaxed write because item can only
|
||||
* be seen if followed by CAS.
|
||||
* Constructs a new node. Uses relaxed write because item can
|
||||
* only be seen after publication via casNext.
|
||||
*/
|
||||
Node(Object item, boolean isData) {
|
||||
UNSAFE.putObject(this, itemOffset, item); // relaxed write
|
||||
@ -422,13 +472,17 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets item to self (using a releasing/lazy write) and waiter
|
||||
* to null, to avoid garbage retention after extracting or
|
||||
* cancelling.
|
||||
* Sets item to self and waiter to null, to avoid garbage
|
||||
* retention after matching or cancelling. Uses relaxed writes
|
||||
* because order is already constrained in the only calling
|
||||
* contexts: item is forgotten only after volatile/atomic
|
||||
* mechanics that extract items. Similarly, clearing waiter
|
||||
* follows either CAS or return from park (if ever parked;
|
||||
* else we don't care).
|
||||
*/
|
||||
final void forgetContents() {
|
||||
UNSAFE.putOrderedObject(this, itemOffset, this);
|
||||
UNSAFE.putOrderedObject(this, waiterOffset, null);
|
||||
UNSAFE.putObject(this, itemOffset, this);
|
||||
UNSAFE.putObject(this, waiterOffset, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -462,7 +516,7 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
* Tries to artificially match a data node -- used by remove.
|
||||
*/
|
||||
final boolean tryMatchData() {
|
||||
// assert isData;
|
||||
// assert isData;
|
||||
Object x = item;
|
||||
if (x != null && x != this && casItem(x, null)) {
|
||||
LockSupport.unpark(waiter);
|
||||
@ -486,12 +540,12 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
/** head of the queue; null until first enqueue */
|
||||
transient volatile Node head;
|
||||
|
||||
/** predecessor of dangling unspliceable node */
|
||||
private transient volatile Node cleanMe; // decl here reduces contention
|
||||
|
||||
/** tail of the queue; null until first append */
|
||||
private transient volatile Node tail;
|
||||
|
||||
/** The number of apparent failures to unsplice removed nodes */
|
||||
private transient volatile int sweepVotes;
|
||||
|
||||
// CAS methods for fields
|
||||
private boolean casTail(Node cmp, Node val) {
|
||||
return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val);
|
||||
@ -501,8 +555,8 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
|
||||
}
|
||||
|
||||
private boolean casCleanMe(Node cmp, Node val) {
|
||||
return UNSAFE.compareAndSwapObject(this, cleanMeOffset, cmp, val);
|
||||
private boolean casSweepVotes(int cmp, int val) {
|
||||
return UNSAFE.compareAndSwapInt(this, sweepVotesOffset, cmp, val);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -515,7 +569,7 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <E> E cast(Object item) {
|
||||
// assert item == null || item.getClass() != Node.class;
|
||||
// assert item == null || item.getClass() != Node.class;
|
||||
return (E) item;
|
||||
}
|
||||
|
||||
@ -544,10 +598,8 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
break;
|
||||
if (p.casItem(item, e)) { // match
|
||||
for (Node q = p; q != h;) {
|
||||
Node n = q.next; // update head by 2
|
||||
if (n != null) // unless singleton
|
||||
q = n;
|
||||
if (head == h && casHead(h, q)) {
|
||||
Node n = q.next; // update by 2 unless singleton
|
||||
if (head == h && casHead(h, n == null? q : n)) {
|
||||
h.forgetNext();
|
||||
break;
|
||||
} // advance and retry
|
||||
@ -632,12 +684,12 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
for (;;) {
|
||||
Object item = s.item;
|
||||
if (item != e) { // matched
|
||||
// assert item != s;
|
||||
// assert item != s;
|
||||
s.forgetContents(); // avoid garbage
|
||||
return this.<E>cast(item);
|
||||
}
|
||||
if ((w.isInterrupted() || (timed && nanos <= 0)) &&
|
||||
s.casItem(e, s)) { // cancel
|
||||
s.casItem(e, s)) { // cancel
|
||||
unsplice(pred, s);
|
||||
return e;
|
||||
}
|
||||
@ -647,9 +699,8 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
randomYields = ThreadLocalRandom.current();
|
||||
}
|
||||
else if (spins > 0) { // spin
|
||||
if (--spins == 0)
|
||||
shortenHeadPath(); // reduce slack before blocking
|
||||
else if (randomYields.nextInt(CHAINED_SPINS) == 0)
|
||||
--spins;
|
||||
if (randomYields.nextInt(CHAINED_SPINS) == 0)
|
||||
Thread.yield(); // occasionally yield
|
||||
}
|
||||
else if (s.waiter == null) {
|
||||
@ -663,8 +714,6 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
}
|
||||
else {
|
||||
LockSupport.park(this);
|
||||
s.waiter = null;
|
||||
spins = -1; // spin if front upon wakeup
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -685,27 +734,6 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries (once) to unsplice nodes between head and first unmatched
|
||||
* or trailing node; failing on contention.
|
||||
*/
|
||||
private void shortenHeadPath() {
|
||||
Node h, hn, p, q;
|
||||
if ((p = h = head) != null && h.isMatched() &&
|
||||
(q = hn = h.next) != null) {
|
||||
Node n;
|
||||
while ((n = q.next) != q) {
|
||||
if (n == null || !q.isMatched()) {
|
||||
if (hn != q && h.next == hn)
|
||||
h.casNext(hn, q);
|
||||
break;
|
||||
}
|
||||
p = q;
|
||||
q = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------- Traversal methods -------------- */
|
||||
|
||||
/**
|
||||
@ -818,7 +846,8 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
public final void remove() {
|
||||
Node p = lastRet;
|
||||
if (p == null) throw new IllegalStateException();
|
||||
findAndRemoveDataNode(lastPred, p);
|
||||
if (p.tryMatchData())
|
||||
unsplice(lastPred, p);
|
||||
}
|
||||
}
|
||||
|
||||
@ -828,99 +857,68 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
* Unsplices (now or later) the given deleted/cancelled node with
|
||||
* the given predecessor.
|
||||
*
|
||||
* @param pred predecessor of node to be unspliced
|
||||
* @param pred a node that was at one time known to be the
|
||||
* predecessor of s, or null or s itself if s is/was at head
|
||||
* @param s the node to be unspliced
|
||||
*/
|
||||
private void unsplice(Node pred, Node s) {
|
||||
s.forgetContents(); // clear unneeded fields
|
||||
final void unsplice(Node pred, Node s) {
|
||||
s.forgetContents(); // forget unneeded fields
|
||||
/*
|
||||
* At any given time, exactly one node on list cannot be
|
||||
* unlinked -- the last inserted node. To accommodate this, if
|
||||
* we cannot unlink s, we save its predecessor as "cleanMe",
|
||||
* processing the previously saved version first. Because only
|
||||
* one node in the list can have a null next, at least one of
|
||||
* node s or the node previously saved can always be
|
||||
* processed, so this always terminates.
|
||||
* See above for rationale. Briefly: if pred still points to
|
||||
* s, try to unlink s. If s cannot be unlinked, because it is
|
||||
* trailing node or pred might be unlinked, and neither pred
|
||||
* nor s are head or offlist, add to sweepVotes, and if enough
|
||||
* votes have accumulated, sweep.
|
||||
*/
|
||||
if (pred != null && pred != s) {
|
||||
while (pred.next == s) {
|
||||
Node oldpred = (cleanMe == null) ? null : reclean();
|
||||
Node n = s.next;
|
||||
if (n != null) {
|
||||
if (n != s)
|
||||
pred.casNext(s, n);
|
||||
break;
|
||||
if (pred != null && pred != s && pred.next == s) {
|
||||
Node n = s.next;
|
||||
if (n == null ||
|
||||
(n != s && pred.casNext(s, n) && pred.isMatched())) {
|
||||
for (;;) { // check if at, or could be, head
|
||||
Node h = head;
|
||||
if (h == pred || h == s || h == null)
|
||||
return; // at head or list empty
|
||||
if (!h.isMatched())
|
||||
break;
|
||||
Node hn = h.next;
|
||||
if (hn == null)
|
||||
return; // now empty
|
||||
if (hn != h && casHead(h, hn))
|
||||
h.forgetNext(); // advance head
|
||||
}
|
||||
if (oldpred == pred || // Already saved
|
||||
((oldpred == null || oldpred.next == s) &&
|
||||
casCleanMe(oldpred, pred))) {
|
||||
break;
|
||||
if (pred.next != pred && s.next != s) { // recheck if offlist
|
||||
for (;;) { // sweep now if enough votes
|
||||
int v = sweepVotes;
|
||||
if (v < SWEEP_THRESHOLD) {
|
||||
if (casSweepVotes(v, v + 1))
|
||||
break;
|
||||
}
|
||||
else if (casSweepVotes(v, 0)) {
|
||||
sweep();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to unsplice the deleted/cancelled node held in cleanMe
|
||||
* that was previously uncleanable because it was at tail.
|
||||
*
|
||||
* @return current cleanMe node (or null)
|
||||
* Unlinks matched (typically cancelled) nodes encountered in a
|
||||
* traversal from head.
|
||||
*/
|
||||
private Node reclean() {
|
||||
/*
|
||||
* cleanMe is, or at one time was, predecessor of a cancelled
|
||||
* node s that was the tail so could not be unspliced. If it
|
||||
* is no longer the tail, try to unsplice if necessary and
|
||||
* make cleanMe slot available. This differs from similar
|
||||
* code in unsplice() because we must check that pred still
|
||||
* points to a matched node that can be unspliced -- if not,
|
||||
* we can (must) clear cleanMe without unsplicing. This can
|
||||
* loop only due to contention.
|
||||
*/
|
||||
Node pred;
|
||||
while ((pred = cleanMe) != null) {
|
||||
Node s = pred.next;
|
||||
Node n;
|
||||
if (s == null || s == pred || !s.isMatched())
|
||||
casCleanMe(pred, null); // already gone
|
||||
else if ((n = s.next) != null) {
|
||||
if (n != s)
|
||||
pred.casNext(s, n);
|
||||
casCleanMe(pred, null);
|
||||
}
|
||||
else
|
||||
private void sweep() {
|
||||
for (Node p = head, s, n; p != null && (s = p.next) != null; ) {
|
||||
if (!s.isMatched())
|
||||
// Unmatched nodes are never self-linked
|
||||
p = s;
|
||||
else if ((n = s.next) == null) // trailing node is pinned
|
||||
break;
|
||||
}
|
||||
return pred;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main implementation of Iterator.remove(). Finds
|
||||
* and unsplices the given data node.
|
||||
*
|
||||
* @param possiblePred possible predecessor of s
|
||||
* @param s the node to remove
|
||||
*/
|
||||
final void findAndRemoveDataNode(Node possiblePred, Node s) {
|
||||
// assert s.isData;
|
||||
if (s.tryMatchData()) {
|
||||
if (possiblePred != null && possiblePred.next == s)
|
||||
unsplice(possiblePred, s); // was actual predecessor
|
||||
else {
|
||||
for (Node pred = null, p = head; p != null; ) {
|
||||
if (p == s) {
|
||||
unsplice(pred, p);
|
||||
break;
|
||||
}
|
||||
if (p.isUnmatchedRequest())
|
||||
break;
|
||||
pred = p;
|
||||
if ((p = p.next) == pred) { // stale
|
||||
pred = null;
|
||||
p = head;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (s == n) // stale
|
||||
// No need to also check for p == s, since that implies s == n
|
||||
p = head;
|
||||
else
|
||||
p.casNext(s, n);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1158,7 +1156,11 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
* @return {@code true} if this queue contains no elements
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return firstOfMode(true) == null;
|
||||
for (Node p = head; p != null; p = succ(p)) {
|
||||
if (!p.isMatched())
|
||||
return !p.isData;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean hasWaitingConsumer() {
|
||||
@ -1252,8 +1254,8 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
objectFieldOffset(UNSAFE, "head", LinkedTransferQueue.class);
|
||||
private static final long tailOffset =
|
||||
objectFieldOffset(UNSAFE, "tail", LinkedTransferQueue.class);
|
||||
private static final long cleanMeOffset =
|
||||
objectFieldOffset(UNSAFE, "cleanMe", LinkedTransferQueue.class);
|
||||
private static final long sweepVotesOffset =
|
||||
objectFieldOffset(UNSAFE, "sweepVotes", LinkedTransferQueue.class);
|
||||
|
||||
static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
|
||||
String field, Class<?> klazz) {
|
||||
@ -1266,5 +1268,4 @@ public class LinkedTransferQueue<E> extends AbstractQueue<E>
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -898,7 +898,7 @@ public class Phaser {
|
||||
boolean doWait() {
|
||||
if (thread != null) {
|
||||
try {
|
||||
ForkJoinPool.managedBlock(this, false);
|
||||
ForkJoinPool.managedBlock(this);
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
|
@ -206,7 +206,7 @@ public final class Integrate {
|
||||
q.fork();
|
||||
ar = recEval(c, r, fc, fr, ar);
|
||||
if (!q.tryUnfork()) {
|
||||
q.quietlyHelpJoin();
|
||||
q.quietlyJoin();
|
||||
return ar + q.area;
|
||||
}
|
||||
return ar + recEval(l, c, fl, fc, al);
|
||||
@ -254,7 +254,7 @@ public final class Integrate {
|
||||
(q = new DQuad(l, c, al)).fork();
|
||||
ar = recEval(c, r, fc, fr, ar);
|
||||
if (q != null && !q.tryUnfork()) {
|
||||
q.quietlyHelpJoin();
|
||||
q.quietlyJoin();
|
||||
return ar + q.area;
|
||||
}
|
||||
return ar + recEval(l, c, fl, fc, al);
|
||||
|
Loading…
x
Reference in New Issue
Block a user