8259800: timeout in tck test testForkJoin(ForkJoinPool8Test)

Reviewed-by: martin, dholmes
This commit is contained in:
Doug Lea 2021-02-22 12:42:40 +00:00
parent 419717ddae
commit 5b7b18c5bf
2 changed files with 241 additions and 172 deletions

View file

@ -521,7 +521,10 @@ public class ForkJoinPool extends AbstractExecutorService {
* terminate others by cancelling their unprocessed tasks, and * terminate others by cancelling their unprocessed tasks, and
* waking them up. Calls to non-abrupt shutdown() preface this by * waking them up. Calls to non-abrupt shutdown() preface this by
* checking isQuiescent before triggering the "STOP" phase of * checking isQuiescent before triggering the "STOP" phase of
* termination. * termination. To conform to ExecutorService invoke, invokeAll,
* and invokeAny specs, we must track pool status while waiting,
* and interrupt interruptible callers on termination (see
* ForkJoinTask.joinForPoolInvoke etc).
* *
* Joining Tasks * Joining Tasks
* ============= * =============
@ -631,6 +634,13 @@ public class ForkJoinPool extends AbstractExecutorService {
* amounts to an odd form of limited spin-wait before blocking in * amounts to an odd form of limited spin-wait before blocking in
* ForkJoinTask.join. * ForkJoinTask.join.
* *
* Guarantees for common pool parallelism zero are limited to
* tasks that are joined by their callers in a tree-structured
* fashion or use CountedCompleters (as is true for jdk
* parallelStreams). Support infiltrates several methods,
* including those that retry helping steps until we are sure that
* none apply if there are no workers.
*
* As a more appropriate default in managed environments, unless * As a more appropriate default in managed environments, unless
* overridden by system properties, we use workers of subclass * overridden by system properties, we use workers of subclass
* InnocuousForkJoinWorkerThread when there is a SecurityManager * InnocuousForkJoinWorkerThread when there is a SecurityManager
@ -893,7 +903,7 @@ public class ForkJoinPool extends AbstractExecutorService {
} }
static final boolean casSlotToNull(ForkJoinTask<?>[] a, int i, static final boolean casSlotToNull(ForkJoinTask<?>[] a, int i,
ForkJoinTask<?> c) { ForkJoinTask<?> c) {
return QA.weakCompareAndSet(a, i, c, null); return QA.compareAndSet(a, i, c, null);
} }
final boolean tryLock() { final boolean tryLock() {
return SOURCE.compareAndSet(this, 0, 1); return SOURCE.compareAndSet(this, 0, 1);
@ -1046,13 +1056,22 @@ public class ForkJoinPool extends AbstractExecutorService {
*/ */
final boolean externalTryUnpush(ForkJoinTask<?> task) { final boolean externalTryUnpush(ForkJoinTask<?> task) {
boolean taken = false; boolean taken = false;
for (;;) {
int s = top, cap, k; ForkJoinTask<?>[] a; int s = top, cap, k; ForkJoinTask<?>[] a;
if ((a = array) != null && (cap = a.length) > 0 && if ((a = array) == null || (cap = a.length) <= 0 ||
a[k = (cap - 1) & (s - 1)] == task && tryLock()) { a[k = (cap - 1) & (s - 1)] != task)
if (top == s && array == a && break;
(taken = casSlotToNull(a, k, task))) if (tryLock()) {
if (top == s && array == a) {
if (taken = casSlotToNull(a, k, task)) {
top = s - 1; top = s - 1;
source = 0; // release lock source = 0;
break;
}
}
source = 0; // release lock for retry
}
Thread.yield(); // trylock failure
} }
return taken; return taken;
} }
@ -1194,15 +1213,16 @@ public class ForkJoinPool extends AbstractExecutorService {
top = s; top = s;
source = 0; source = 0;
} }
if (taken)
t.doExec();
else if (!owned)
Thread.yield(); // tryLock failure
break; break;
} }
else if ((f = f.completer) == null) else if ((f = f.completer) == null)
break; break;
} }
if (!taken) if (taken && limit != 0 && --limit == 0)
break;
t.doExec();
if (limit != 0 && --limit == 0)
break; break;
} }
return status; return status;
@ -1586,7 +1606,7 @@ public class ForkJoinPool extends AbstractExecutorService {
* @param w caller's WorkQueue (may be null on failed initialization) * @param w caller's WorkQueue (may be null on failed initialization)
*/ */
final void runWorker(WorkQueue w) { final void runWorker(WorkQueue w) {
if (w != null) { // skip on failed init if (mode >= 0 && w != null) { // skip on failed init
w.config |= SRC; // mark as valid source w.config |= SRC; // mark as valid source
int r = w.stackPred, src = 0; // use seed from registerWorker int r = w.stackPred, src = 0; // use seed from registerWorker
do { do {
@ -1710,22 +1730,6 @@ public class ForkJoinPool extends AbstractExecutorService {
// Utilities used by ForkJoinTask // Utilities used by ForkJoinTask
/**
* Returns true if all workers are busy, possibly creating one if allowed
*/
final boolean isSaturated() {
int maxTotal = bounds >>> SWIDTH;
for (long c;;) {
if (((int)(c = ctl) & ~UNSIGNALLED) != 0)
return false;
if ((short)(c >>> TC_SHIFT) >= maxTotal)
return true;
long nc = ((c + TC_UNIT) & TC_MASK) | (c & ~TC_MASK);
if (compareAndSetCtl(c, nc))
return !createWorker();
}
}
/** /**
* Returns true if can start terminating if enabled, or already terminated * Returns true if can start terminating if enabled, or already terminated
*/ */
@ -1765,13 +1769,16 @@ public class ForkJoinPool extends AbstractExecutorService {
*/ */
private int tryCompensate(long c) { private int tryCompensate(long c) {
Predicate<? super ForkJoinPool> sat; Predicate<? super ForkJoinPool> sat;
int b = bounds; // counts are signed; centered at parallelism level == 0 int md = mode, b = bounds;
// counts are signed; centered at parallelism level == 0
int minActive = (short)(b & SMASK), int minActive = (short)(b & SMASK),
maxTotal = b >>> SWIDTH, maxTotal = b >>> SWIDTH,
active = (int)(c >> RC_SHIFT), active = (int)(c >> RC_SHIFT),
total = (short)(c >>> TC_SHIFT), total = (short)(c >>> TC_SHIFT),
sp = (int)c & ~UNSIGNALLED; sp = (int)c & ~UNSIGNALLED;
if (total >= 0) { if ((md & SMASK) == 0)
return 0; // cannot compensate if parallelism zero
else if (total >= 0) {
if (sp != 0) { // activate idle worker if (sp != 0) { // activate idle worker
WorkQueue[] qs; int n; WorkQueue v; WorkQueue[] qs; int n; WorkQueue v;
if ((qs = queues) != null && (n = qs.length) > 0 && if ((qs = queues) != null && (n = qs.length) > 0 &&
@ -1819,9 +1826,10 @@ public class ForkJoinPool extends AbstractExecutorService {
* *
* @param task the task * @param task the task
* @param w caller's WorkQueue * @param w caller's WorkQueue
* @param canHelp if false, compensate only
* @return task status on exit, or UNCOMPENSATE for compensated blocking * @return task status on exit, or UNCOMPENSATE for compensated blocking
*/ */
final int helpJoin(ForkJoinTask<?> task, WorkQueue w) { final int helpJoin(ForkJoinTask<?> task, WorkQueue w, boolean canHelp) {
int s = 0; int s = 0;
if (task != null && w != null) { if (task != null && w != null) {
int wsrc = w.source, wid = w.config & SMASK, r = wid + 2; int wsrc = w.source, wid = w.config & SMASK, r = wid + 2;
@ -1836,7 +1844,7 @@ public class ForkJoinPool extends AbstractExecutorService {
else if (c == (c = ctl) && (s = tryCompensate(c)) >= 0) else if (c == (c = ctl) && (s = tryCompensate(c)) >= 0)
break; // block break; // block
} }
else { // scan for subtasks else if (canHelp) { // scan for subtasks
WorkQueue[] qs = queues; WorkQueue[] qs = queues;
int n = (qs == null) ? 0 : qs.length, m = n - 1; int n = (qs == null) ? 0 : qs.length, m = n - 1;
for (int i = n; i > 0; i -= 2, r += 2) { for (int i = n; i > 0; i -= 2, r += 2) {
@ -2194,6 +2202,16 @@ public class ForkJoinPool extends AbstractExecutorService {
qs[(n - 1) & (r << 1)] : null; qs[(n - 1) & (r << 1)] : null;
} }
/**
* Returns queue for an external thread, if one exists
*/
final WorkQueue externalQueue() {
WorkQueue[] qs;
int r = ThreadLocalRandom.getProbe(), n;
return ((qs = queues) != null && (n = qs.length) > 0 && r != 0) ?
qs[(n - 1) & (r << 1)] : null;
}
/** /**
* If the given executor is a ForkJoinPool, poll and execute * If the given executor is a ForkJoinPool, poll and execute
* AsynchronousCompletionTasks from worker's queue until none are * AsynchronousCompletionTasks from worker's queue until none are
@ -2205,8 +2223,8 @@ public class ForkJoinPool extends AbstractExecutorService {
if ((wt = (ForkJoinWorkerThread)t).pool == e) if ((wt = (ForkJoinWorkerThread)t).pool == e)
w = wt.workQueue; w = wt.workQueue;
} }
else if (e == common) else if (e instanceof ForkJoinPool)
w = commonQueue(); w = ((ForkJoinPool)e).externalQueue();
if (w != null) if (w != null)
w.helpAsyncBlocker(blocker); w.helpAsyncBlocker(blocker);
} }
@ -2292,14 +2310,18 @@ public class ForkJoinPool extends AbstractExecutorService {
return false; return false;
md = getAndBitwiseOrMode(STOP); md = getAndBitwiseOrMode(STOP);
} }
for (int k = 0; k < 2; ++k) { // twice in case of lagging qs updates for (boolean rescan = true;;) { // repeat until no changes
for (ForkJoinTask<?> t; (t = pollScan(false)) != null; ) boolean changed = false;
for (ForkJoinTask<?> t; (t = pollScan(false)) != null; ) {
changed = true;
ForkJoinTask.cancelIgnoringExceptions(t); // help cancel ForkJoinTask.cancelIgnoringExceptions(t); // help cancel
}
WorkQueue[] qs; int n; WorkQueue q; Thread thread; WorkQueue[] qs; int n; WorkQueue q; Thread thread;
if ((qs = queues) != null && (n = qs.length) > 0) { if ((qs = queues) != null && (n = qs.length) > 0) {
for (int j = 1; j < n; j += 2) { // unblock other workers for (int j = 1; j < n; j += 2) { // unblock other workers
if ((q = qs[j]) != null && (thread = q.owner) != null && if ((q = qs[j]) != null && (thread = q.owner) != null &&
!thread.isInterrupted()) { !thread.isInterrupted()) {
changed = true;
try { try {
thread.interrupt(); thread.interrupt();
} catch (Throwable ignore) { } catch (Throwable ignore) {
@ -2317,6 +2339,12 @@ public class ForkJoinPool extends AbstractExecutorService {
cond.signalAll(); cond.signalAll();
lock.unlock(); lock.unlock();
} }
if (changed)
rescan = true;
else if (rescan)
rescan = false;
else
break;
} }
return true; return true;
} }
@ -2539,17 +2567,23 @@ public class ForkJoinPool extends AbstractExecutorService {
parallelism = Integer.parseInt(pp); parallelism = Integer.parseInt(pp);
} catch (Exception ignore) { } catch (Exception ignore) {
} }
int p = this.mode = Math.min(Math.max(parallelism, 0), MAX_CAP);
int size = 1 << (33 - Integer.numberOfLeadingZeros(p > 0 ? p - 1 : 1));
this.factory = (fac != null) ? fac :
new DefaultCommonPoolForkJoinWorkerThreadFactory();
this.ueh = handler; this.ueh = handler;
this.keepAlive = DEFAULT_KEEPALIVE; this.keepAlive = DEFAULT_KEEPALIVE;
this.saturate = null; this.saturate = null;
this.workerNamePrefix = null; this.workerNamePrefix = null;
int p = Math.min(Math.max(parallelism, 0), MAX_CAP), size;
if (p > 0) {
size = 1 << (33 - Integer.numberOfLeadingZeros(p - 1));
this.bounds = ((1 - p) & SMASK) | (COMMON_MAX_SPARES << SWIDTH); this.bounds = ((1 - p) & SMASK) | (COMMON_MAX_SPARES << SWIDTH);
this.ctl = ((((long)(-p) << TC_SHIFT) & TC_MASK) | this.ctl = ((((long)(-p) << TC_SHIFT) & TC_MASK) |
(((long)(-p) << RC_SHIFT) & RC_MASK)); (((long)(-p) << RC_SHIFT) & RC_MASK));
} else { // zero min, max, spare counts, 1 slot
size = 1;
this.bounds = 0;
this.ctl = 0L;
}
this.factory = (fac != null) ? fac :
new DefaultCommonPoolForkJoinWorkerThreadFactory();
this.queues = new WorkQueue[size]; this.queues = new WorkQueue[size];
this.registrationLock = new ReentrantLock(); this.registrationLock = new ReentrantLock();
} }
@ -2593,7 +2627,7 @@ public class ForkJoinPool extends AbstractExecutorService {
*/ */
public <T> T invoke(ForkJoinTask<T> task) { public <T> T invoke(ForkJoinTask<T> task) {
externalSubmit(task); externalSubmit(task);
return task.join(); return task.joinForPoolInvoke(this);
} }
/** /**
@ -2685,7 +2719,7 @@ public class ForkJoinPool extends AbstractExecutorService {
externalSubmit(f); externalSubmit(f);
} }
for (int i = futures.size() - 1; i >= 0; --i) for (int i = futures.size() - 1; i >= 0; --i)
((ForkJoinTask<?>)futures.get(i)).quietlyJoin(); ((ForkJoinTask<?>)futures.get(i)).awaitPoolInvoke(this);
return futures; return futures;
} catch (Throwable t) { } catch (Throwable t) {
for (Future<T> e : futures) for (Future<T> e : futures)
@ -2715,11 +2749,7 @@ public class ForkJoinPool extends AbstractExecutorService {
if (timedOut) if (timedOut)
ForkJoinTask.cancelIgnoringExceptions(f); ForkJoinTask.cancelIgnoringExceptions(f);
else { else {
try { ((ForkJoinTask<T>)f).awaitPoolInvoke(this, ns);
f.get(ns, TimeUnit.NANOSECONDS);
} catch (CancellationException | TimeoutException |
ExecutionException ok) {
}
if ((ns = nanos - (System.nanoTime() - startTime)) < 0L) if ((ns = nanos - (System.nanoTime() - startTime)) < 0L)
timedOut = true; timedOut = true;
} }
@ -2746,11 +2776,16 @@ public class ForkJoinPool extends AbstractExecutorService {
} }
final void tryComplete(Callable<E> c) { // called by InvokeAnyTasks final void tryComplete(Callable<E> c) { // called by InvokeAnyTasks
Throwable ex = null; Throwable ex = null;
boolean failed = (c == null || isCancelled() || boolean failed;
(pool != null && pool.mode < 0)); if (c == null || Thread.interrupted() ||
if (!failed && !isDone()) { (pool != null && pool.mode < 0))
failed = true;
else if (isDone())
failed = false;
else {
try { try {
complete(c.call()); complete(c.call());
failed = false;
} catch (Throwable tx) { } catch (Throwable tx) {
ex = tx; ex = tx;
failed = true; failed = true;
@ -2817,7 +2852,7 @@ public class ForkJoinPool extends AbstractExecutorService {
if (root.isDone()) if (root.isDone())
break; break;
} }
return root.get(); return root.getForPoolInvoke(this);
} finally { } finally {
for (InvokeAnyTask<T> f : fs) for (InvokeAnyTask<T> f : fs)
ForkJoinTask.cancelIgnoringExceptions(f); ForkJoinTask.cancelIgnoringExceptions(f);
@ -2844,7 +2879,7 @@ public class ForkJoinPool extends AbstractExecutorService {
if (root.isDone()) if (root.isDone())
break; break;
} }
return root.get(nanos, TimeUnit.NANOSECONDS); return root.getForPoolInvoke(this, nanos);
} finally { } finally {
for (InvokeAnyTask<T> f : fs) for (InvokeAnyTask<T> f : fs)
ForkJoinTask.cancelIgnoringExceptions(f); ForkJoinTask.cancelIgnoringExceptions(f);

View file

@ -276,7 +276,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
return (int)STATUS.getAndBitwiseOr(this, v); return (int)STATUS.getAndBitwiseOr(this, v);
} }
private boolean casStatus(int c, int v) { private boolean casStatus(int c, int v) {
return STATUS.weakCompareAndSet(this, c, v); return STATUS.compareAndSet(this, c, v);
} }
private boolean casAux(Aux c, Aux v) { private boolean casAux(Aux c, Aux v) {
return AUX.compareAndSet(this, c, v); return AUX.compareAndSet(this, c, v);
@ -295,84 +295,6 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
} }
} }
/**
* Possibly blocks until task is done or interrupted or timed out.
*
* @param interruptible true if wait can be cancelled by interrupt
* @param deadline if non-zero use timed waits and possibly timeout
* @param pool if nonnull pool to uncompensate after unblocking
* @return status on exit, or ABNORMAL if interrupted while waiting
*/
private int awaitDone(boolean interruptible, long deadline,
ForkJoinPool pool) {
int s;
boolean interrupted = false, queued = false, parked = false;
Aux node = null;
while ((s = status) >= 0) {
Aux a; long ns;
if (parked && Thread.interrupted()) {
if (interruptible) {
s = ABNORMAL;
break;
}
interrupted = true;
}
else if (queued) {
if (deadline != 0L) {
if ((ns = deadline - System.nanoTime()) <= 0L)
break;
LockSupport.parkNanos(ns);
}
else
LockSupport.park();
parked = true;
}
else if (node != null) {
if ((a = aux) != null && a.ex != null)
Thread.onSpinWait(); // exception in progress
else if (queued = casAux(node.next = a, node))
LockSupport.setCurrentBlocker(this);
}
else {
try {
node = new Aux(Thread.currentThread(), null);
} catch (Throwable ex) { // try to cancel if cannot create
casStatus(s, s | (DONE | ABNORMAL));
}
}
}
if (pool != null)
pool.uncompensate();
if (queued) {
LockSupport.setCurrentBlocker(null);
if (s >= 0) { // cancellation similar to AbstractQueuedSynchronizer
outer: for (Aux a; (a = aux) != null && a.ex == null; ) {
for (Aux trail = null;;) {
Aux next = a.next;
if (a == node) {
if (trail != null)
trail.casNext(trail, next);
else if (casAux(a, next))
break outer; // cannot be re-encountered
break; // restart
} else {
trail = a;
if ((a = next) == null)
break outer;
}
}
}
}
else {
signalWaiters(); // help clean or signal
if (interrupted)
Thread.currentThread().interrupt();
}
}
return s;
}
/** /**
* Sets DONE status and wakes up threads waiting to join this task. * Sets DONE status and wakes up threads waiting to join this task.
* @return status on exit * @return status on exit
@ -463,27 +385,36 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
* Helps and/or waits for completion from join, get, or invoke; * Helps and/or waits for completion from join, get, or invoke;
* called from either internal or external threads. * called from either internal or external threads.
* *
* @param pool if nonnull, known submitted pool, else assumes current pool
* @param ran true if task known to have been exec'd * @param ran true if task known to have been exec'd
* @param interruptible true if park interruptibly when external * @param interruptible true if park interruptibly when external
* @param timed true if use timed wait * @param timed true if use timed wait
* @param nanos if timed, timeout value * @param nanos if timed, timeout value
* @return ABNORMAL if interrupted, else status on exit * @return ABNORMAL if interrupted, else status on exit
*/ */
private int awaitJoin(boolean ran, boolean interruptible, boolean timed, private int awaitDone(ForkJoinPool pool, boolean ran,
boolean interruptible, boolean timed,
long nanos) { long nanos) {
boolean internal; ForkJoinPool p; ForkJoinPool.WorkQueue q; int s; ForkJoinPool p; boolean internal; int s; Thread t;
Thread t; ForkJoinWorkerThread wt; ForkJoinPool.WorkQueue q = null;
if (internal = ((t = Thread.currentThread()) if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) {
instanceof ForkJoinWorkerThread)) { ForkJoinWorkerThread wt = (ForkJoinWorkerThread)t;
p = (wt = (ForkJoinWorkerThread)t).pool; p = wt.pool;
if (pool == null)
pool = p;
if (internal = (pool == p))
q = wt.workQueue; q = wt.workQueue;
} }
else { else {
internal = false;
p = ForkJoinPool.common; p = ForkJoinPool.common;
q = ForkJoinPool.commonQueue(); if (pool == null)
pool = p;
if (pool == p && p != null)
q = p.externalQueue();
}
if (interruptible && Thread.interrupted()) if (interruptible && Thread.interrupted())
return ABNORMAL; return ABNORMAL;
}
if ((s = status) < 0) if ((s = status) < 0)
return s; return s;
long deadline = 0L; long deadline = 0L;
@ -493,22 +424,94 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
else if ((deadline = nanos + System.nanoTime()) == 0L) else if ((deadline = nanos + System.nanoTime()) == 0L)
deadline = 1L; deadline = 1L;
} }
ForkJoinPool uncompensate = null; boolean uncompensate = false;
if (q != null && p != null) { // try helping if (q != null && p != null) { // try helping
if ((!timed || p.isSaturated()) && // help even in timed mode if pool has no parallelism
((this instanceof CountedCompleter) ? boolean canHelp = !timed || (p.mode & SMASK) == 0;
(s = p.helpComplete(this, q, internal)) < 0 : if (canHelp) {
(q.tryRemove(this, internal) && (s = doExec()) < 0))) if ((this instanceof CountedCompleter) &&
(s = p.helpComplete(this, q, internal)) < 0)
return s; return s;
if (!ran && ((!internal && q.externalTryUnpush(this)) ||
q.tryRemove(this, internal)) && (s = doExec()) < 0)
return s;
}
if (internal) { if (internal) {
if ((s = p.helpJoin(this, q)) < 0) if ((s = p.helpJoin(this, q, canHelp)) < 0)
return s; return s;
if (s == UNCOMPENSATE) if (s == UNCOMPENSATE)
uncompensate = p; uncompensate = true;
interruptible = false;
} }
} }
return awaitDone(interruptible, deadline, uncompensate); // block until done or cancelled wait
boolean interrupted = false, queued = false;
boolean parked = false, fail = false;
Aux node = null;
while ((s = status) >= 0) {
Aux a; long ns;
if (fail || (fail = (pool != null && pool.mode < 0)))
casStatus(s, s | (DONE | ABNORMAL)); // try to cancel
else if (parked && Thread.interrupted()) {
if (interruptible) {
s = ABNORMAL;
break;
}
interrupted = true;
}
else if (queued) {
if (deadline != 0L) {
if ((ns = deadline - System.nanoTime()) <= 0L)
break;
LockSupport.parkNanos(ns);
}
else
LockSupport.park();
parked = true;
}
else if (node != null) {
if ((a = aux) != null && a.ex != null)
Thread.onSpinWait(); // exception in progress
else if (queued = casAux(node.next = a, node))
LockSupport.setCurrentBlocker(this);
}
else {
try {
node = new Aux(Thread.currentThread(), null);
} catch (Throwable ex) { // cannot create
fail = true;
}
}
}
if (pool != null && uncompensate)
pool.uncompensate();
if (queued) {
LockSupport.setCurrentBlocker(null);
if (s >= 0) { // cancellation similar to AbstractQueuedSynchronizer
outer: for (Aux a; (a = aux) != null && a.ex == null; ) {
for (Aux trail = null;;) {
Aux next = a.next;
if (a == node) {
if (trail != null)
trail.casNext(trail, next);
else if (casAux(a, next))
break outer; // cannot be re-encountered
break; // restart
} else {
trail = a;
if ((a = next) == null)
break outer;
}
}
}
}
else {
signalWaiters(); // help clean or signal
if (interrupted)
Thread.currentThread().interrupt();
}
}
return s;
} }
/** /**
@ -664,7 +667,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
public final V join() { public final V join() {
int s; int s;
if ((s = status) >= 0) if ((s = status) >= 0)
s = awaitJoin(false, false, false, 0L); s = awaitDone(null, false, false, false, 0L);
if ((s & ABNORMAL) != 0) if ((s & ABNORMAL) != 0)
reportException(s); reportException(s);
return getRawResult(); return getRawResult();
@ -681,7 +684,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
public final V invoke() { public final V invoke() {
int s; int s;
if ((s = doExec()) >= 0) if ((s = doExec()) >= 0)
s = awaitJoin(true, false, false, 0L); s = awaitDone(null, true, false, false, 0L);
if ((s & ABNORMAL) != 0) if ((s & ABNORMAL) != 0)
reportException(s); reportException(s);
return getRawResult(); return getRawResult();
@ -710,12 +713,12 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
throw new NullPointerException(); throw new NullPointerException();
t2.fork(); t2.fork();
if ((s1 = t1.doExec()) >= 0) if ((s1 = t1.doExec()) >= 0)
s1 = t1.awaitJoin(true, false, false, 0L); s1 = t1.awaitDone(null, true, false, false, 0L);
if ((s1 & ABNORMAL) != 0) { if ((s1 & ABNORMAL) != 0) {
cancelIgnoringExceptions(t2); cancelIgnoringExceptions(t2);
t1.reportException(s1); t1.reportException(s1);
} }
else if (((s2 = t2.awaitJoin(false, false, false, 0L)) & ABNORMAL) != 0) else if (((s2 = t2.awaitDone(null, false, false, false, 0L)) & ABNORMAL) != 0)
t2.reportException(s2); t2.reportException(s2);
} }
@ -746,7 +749,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
if (i == 0) { if (i == 0) {
int s; int s;
if ((s = t.doExec()) >= 0) if ((s = t.doExec()) >= 0)
s = t.awaitJoin(true, false, false, 0L); s = t.awaitDone(null, true, false, false, 0L);
if ((s & ABNORMAL) != 0) if ((s & ABNORMAL) != 0)
ex = t.getException(s); ex = t.getException(s);
break; break;
@ -759,7 +762,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
if ((t = tasks[i]) != null) { if ((t = tasks[i]) != null) {
int s; int s;
if ((s = t.status) >= 0) if ((s = t.status) >= 0)
s = t.awaitJoin(false, false, false, 0L); s = t.awaitDone(null, false, false, false, 0L);
if ((s & ABNORMAL) != 0 && (ex = t.getException(s)) != null) if ((s & ABNORMAL) != 0 && (ex = t.getException(s)) != null)
break; break;
} }
@ -809,7 +812,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
if (i == 0) { if (i == 0) {
int s; int s;
if ((s = t.doExec()) >= 0) if ((s = t.doExec()) >= 0)
s = t.awaitJoin(true, false, false, 0L); s = t.awaitDone(null, true, false, false, 0L);
if ((s & ABNORMAL) != 0) if ((s & ABNORMAL) != 0)
ex = t.getException(s); ex = t.getException(s);
break; break;
@ -822,7 +825,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
if ((t = ts.get(i)) != null) { if ((t = ts.get(i)) != null) {
int s; int s;
if ((s = t.status) >= 0) if ((s = t.status) >= 0)
s = t.awaitJoin(false, false, false, 0L); s = t.awaitDone(null, false, false, false, 0L);
if ((s & ABNORMAL) != 0 && (ex = t.getException(s)) != null) if ((s & ABNORMAL) != 0 && (ex = t.getException(s)) != null)
break; break;
} }
@ -973,8 +976,8 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
* member of a ForkJoinPool and was interrupted while waiting * member of a ForkJoinPool and was interrupted while waiting
*/ */
public final V get() throws InterruptedException, ExecutionException { public final V get() throws InterruptedException, ExecutionException {
int s; int s = awaitDone(null, false, true, false, 0L);
if (((s = awaitJoin(false, true, false, 0L)) & ABNORMAL) != 0) if ((s & ABNORMAL) != 0)
reportExecutionException(s); reportExecutionException(s);
return getRawResult(); return getRawResult();
} }
@ -995,9 +998,9 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
*/ */
public final V get(long timeout, TimeUnit unit) public final V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException { throws InterruptedException, ExecutionException, TimeoutException {
int s; long nanos = unit.toNanos(timeout);
if ((s = awaitJoin(false, true, true, unit.toNanos(timeout))) >= 0 || int s = awaitDone(null, false, true, true, nanos);
(s & ABNORMAL) != 0) if (s >= 0 || (s & ABNORMAL) != 0)
reportExecutionException(s); reportExecutionException(s);
return getRawResult(); return getRawResult();
} }
@ -1010,9 +1013,10 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
*/ */
public final void quietlyJoin() { public final void quietlyJoin() {
if (status >= 0) if (status >= 0)
awaitJoin(false, false, false, 0L); awaitDone(null, false, false, false, 0L);
} }
/** /**
* Commences performing this task and awaits its completion if * Commences performing this task and awaits its completion if
* necessary, without returning its result or throwing its * necessary, without returning its result or throwing its
@ -1020,7 +1024,37 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
*/ */
public final void quietlyInvoke() { public final void quietlyInvoke() {
if (doExec() >= 0) if (doExec() >= 0)
awaitJoin(true, false, false, 0L); awaitDone(null, true, false, false, 0L);
}
// Versions of join/get for pool.invoke* methods that use external,
// possibly-non-commonPool submits
final void awaitPoolInvoke(ForkJoinPool pool) {
awaitDone(pool, false, false, false, 0L);
}
final void awaitPoolInvoke(ForkJoinPool pool, long nanos) {
awaitDone(pool, false, true, true, nanos);
}
final V joinForPoolInvoke(ForkJoinPool pool) {
int s = awaitDone(pool, false, false, false, 0L);
if ((s & ABNORMAL) != 0)
reportException(s);
return getRawResult();
}
final V getForPoolInvoke(ForkJoinPool pool)
throws InterruptedException, ExecutionException {
int s = awaitDone(pool, false, true, false, 0L);
if ((s & ABNORMAL) != 0)
reportExecutionException(s);
return getRawResult();
}
final V getForPoolInvoke(ForkJoinPool pool, long nanos)
throws InterruptedException, ExecutionException, TimeoutException {
int s = awaitDone(pool, false, true, true, nanos);
if (s >= 0 || (s & ABNORMAL) != 0)
reportExecutionException(s);
return getRawResult();
} }
/** /**