/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/licenses/publicdomain
*/
package jsr166y.forkjoin;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
/**
* Adapter class to allow tasks submitted to a pool to act as Futures.
* Methods are implemented in the same way as in the RecursiveTask
* class, but with extra bookkeeping and signalling to cover three
* kinds of adaptation:
*
* (1) Unlike internal fork/join processing, get() must block if the
* caller is a normal thread (not FJ worker thread). We use a simpler
* variant of the mechanics used in FutureTask, but bypass them and
* use helping joins if the caller is itself a ForkJoinWorkerThread.
*
* (2) Regular Futures encase RuntimeExceptions within
* ExecutionExceptions, while internal tasks just throw them directly,
* so these must be trapped and wrapped.
*
* (3) External submissions are tracked for the sake of managing
* worker threads. The pool submissionStarting and submissionCompleted
* methods perform the associated bookkeeping. This requires some care
* with cancellation and early termination -- the completion signal
* can be issued only if a start signal ever was.
*
*/
final class Submission<V> extends ForkJoinTask<V> implements Future<V> {
// Status values for sync. We need to keep track of RUNNING status
// just to make sure callbacks to pool are balanced.
static final int INITIAL = 0;
static final int RUNNING = 1;
static final int DONE = 2;
/**
* Stripped-down variant of FutureTask.sync
*/
static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
public int tryAcquireShared(int acquires) {
return getState() == DONE? 1 : -1;
}
public boolean tryReleaseShared(int releases) { return true; }
public void reset() { setState(INITIAL); }
public boolean isDone() { return getState() == DONE; }
public boolean transitionToRunning() {
return compareAndSetState(INITIAL, RUNNING);
}
/** Set status to DONE, release waiters, and return old state */
public int transitionToDone() {
for (;;) {
int c = getState();
if (c == DONE || compareAndSetState(c, DONE)) {
releaseShared(0);
return c;
}
}
}
}
private final ForkJoinTask<V> task;
private final ForkJoinPool pool;
private final Sync sync;
private V result;
Submission(ForkJoinTask<V> t, ForkJoinPool p) {
task = t;
pool = p;
sync = new Sync();
}
/**
* Run the inner tssk.
*/
private void runTask() {
try {
if (sync.transitionToRunning()) {
pool.submissionStarting();
if (status >= 0) {
result = task.forkJoin();
setDone();
}
}
} catch(Throwable rex) {
setDoneExceptionally(rex);
} finally {
if (sync.transitionToDone() == RUNNING)
pool.submissionCompleted();
}
}
public V forkJoin() {
runTask();
return reportAsForkJoinResult();
}
final boolean exec() {
runTask();
return completedNormally();
}
public V rawResult() {
return result;
}
/**
* ForkJoinTask version of cancel
*/
public void cancel() {
try {
if (!sync.isDone()) {
setCancelled(); // avoid recursive call to cancel
task.cancel();
}
} finally {
// Claim completion even if async cancel
if (sync.transitionToDone() == RUNNING)
pool.submissionCompleted();
}
}
/**
* Future version of cancel
*/
public boolean cancel(boolean ignore) {
this.cancel();
return isCancelled();
}
public V get() throws InterruptedException, ExecutionException {
// If caller is FJ worker, help instead of block, but fall
// through.to acquire, to preserve Submission sync guarantees
Thread t = Thread.currentThread();
if (t instanceof ForkJoinWorkerThread)
quietlyJoin();
sync.acquireSharedInterruptibly(1);
return reportAsFutureResult();
}
public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
long nanos = unit.toNanos(timeout);
Thread t = Thread.currentThread();
if (t instanceof ForkJoinWorkerThread) {
if(!((ForkJoinWorkerThread)t).doTimedJoinTask(this, nanos))
throw new TimeoutException();
// Preserve Submission sync guarantees
sync.acquireSharedInterruptibly(1);
}
else if (!sync.tryAcquireSharedNanos(1, nanos))
throw new TimeoutException();
return reportAsFutureResult();
}
/**
* Interrupt-less get for ForkJoinPool.invoke
*/
public V awaitInvoke() {
Thread t = Thread.currentThread();
if (t instanceof ForkJoinWorkerThread)
quietlyJoin();
sync.acquireShared(1);
return reportAsForkJoinResult();
}
public void reinitialize() { // Required, but of dubious value.
result = null;
sync.reset();
super.reinitialize();
}
/**
* Within-package utility to access underlying task
*/
ForkJoinTask<V> getSubmittedTask() {
return task;
}
/**
* Externally set completion
*/
void finishTask(V value) {
if (sync.transitionToRunning())
pool.submissionStarting();
result = value;
setDone();
if (sync.transitionToDone() == RUNNING)
pool.submissionCompleted();
}
/**
* Externally set exceptional completion
*/
void finishTaskExceptionally(Throwable rex) {
if (sync.transitionToRunning())
pool.submissionStarting();
setDoneExceptionally(rex);
if (sync.transitionToDone() == RUNNING)
pool.submissionCompleted();
}
}