/*
* 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.io.Serializable;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import sun.misc.Unsafe;
import java.lang.reflect.*;
/**
* Abstract base class for tasks that run within a ForkJoinPool. A
* ForkJoinTask is a thread-like entity that is much lighter weight
* than a normal thread. Huge numbers of tasks and subtasks may be
* hosted by a small number of actual threads in a ForkJoinPool,
* at the price of some usage limitations.
*
* <p> The <tt>ForkJoinTask</tt> class is not directly subclassable
* outside of this package. Instead, you can subclass one of the
* supplied abstract classes that support various styles of fork/join
* processing. Normally, a concrete ForkJoinTask subclass declares
* fields comprising its parameters, established in a constructor, and
* then defines a <tt>compute</tt> method that somehow uses the
* control methods supplied by this base class. While these methods
* have <tt>public</tt> access, most may only be called from within
* other ForkJoinTasks. Attempts to invoke them in other contexts
* result in exceptions or errors including ClassCastException. The
* only generally accessible methods are those for cancellation and
* status checking. The only way to invoke a "main" driver task is to
* submit it to a ForkJoinPool. Normally, once started, this will in
* turn start other subtasks. Nearly all of these base support
* methods are <tt>final</tt> because their implementations are
* intrinsically tied to the underlying lightweight task scheduling
* framework, and so cannot be overridden.
*
* <p> ForkJoinTasks play similar roles as <tt>Futures</tt> but
* support a more limited range of use. The "lightness" of
* ForkJoinTasks is due to a set of restrictions (that are only
* partially statically enforceable) reflecting their intended use as
* purely computational tasks -- calculating pure functions or
* operating on purely isolated objects. The only coordination
* mechanisms supported for ForkJoinTasks are <tt>fork</tt>, that
* arranges asynchronous execution, and <tt>join</tt>, that doesn't
* proceed until the task's result has been computed. (A simple form
* of cancellation is also supported). The computation defined in the
* <tt>compute</tt> method should not in general perform any other
* form of blocking synchronization, should not perform IO, and should
* be independent of other tasks. Minor breaches of these
* restrictions, for example using shared output streams, may be
* tolerable in practice, but frequent use will result in poor
* performance, and the potential to indefinitely stall if the number
* of threads not waiting for external synchronization becomes
* exhausted. This usage restriction is in part enforced by not
* permitting checked exceptions such as IOExceptions to be
* thrown. However, computations may still encounter unchecked
* exceptions, that are rethrown to callers attempting join
* them. These exceptions may additionally include
* RejectedExecutionExceptions stemming from internal resource
* exhaustion such as failure to allocate internal task queues.
*
* <p>ForkJoinTasks should perform relatively small amounts of
* computations, othewise splitting into smaller tasks. As a very
* rough rule of thumb, a task should perform more than 100 and less
* than 10000 basic computational steps. If tasks are too big, then
* parellelism cannot improve throughput. If too small, then memory
* and internal task maintenance overhead may overwhelm processing.
* The {@link ForkJoinWorkerThread} class supports a number of
* inspection and tuning methods that can be useful when developing
* fork/join programs.
*
* <p>ForkJoinTasks are <tt>Serializable</tt>, which enables them to
* be used in extensions such as remote execution frameworks. However,
* it is in general safe to serialize tasks only before or after, but
* not during execution. Serialization is not relied on during
* execution itself.
*/
public abstract class ForkJoinTask<V> implements Serializable {
/*
* The main implementations of execution methods are provided by
* ForkJoinWorkerThread, so internals are package protected. This
* class is mainly responsible for maintaining task status field
* and exception mechanics.
*/
/**
* Table of exceptions thrown by tasks, to enable reporting by
* callers. Because exceptions are rare, we don't directly keep
* them with task objects, but instead us a weak ref table. Note
* that cancellation exceptions don't appear in the table, but are
* instead recorded as status values.
*
* Todo: Use ConcurrentReferenceMap
*/
static final WeakHashMap<ForkJoinTask<?>, Throwable> exceptionTable =
new WeakHashMap<ForkJoinTask<?>, Throwable>();
static synchronized void setException(ForkJoinTask<?> t, Throwable ex) {
exceptionTable.put(t, ex);
}
static synchronized Throwable getException(ForkJoinTask<?> t) {
return exceptionTable.get(t);
}
static synchronized void clearException(ForkJoinTask<?> t) {
exceptionTable.remove(t);
}
/**
* Disallow direct construction outside this package.
*/
ForkJoinTask() {
}
/**
* Status, taking values:
* sero: initial
* negative: COMPLETED. CANCELLED, or HAS_EXCEPTION
* positive: ignored wrt completion, may be used by subclasses
*
* Status is set negative when a task completes. For normal
* completion, the status field is set with a cheaper ordered
* write (as opposed to volatile write) because ownership
* maintenance in Worker queues ensures that it is never subject
* to read-after-write anomalies. (Implementing this requires
* direct use of Unsafe to avoid overhead.)
*/
volatile int status;
static final int COMPLETED = -1; // order matters
static final int CANCELLED = -2;
static final int HAS_EXCEPTION = -3;
// within-package utilities
/**
* Immediately executes this task unless already complete,
* trapping all possible exceptions. Returns true if executed and
* completed normally, else false. This method cannot be
* implemented outside this package because we must guarantee that
* implementations trap all exceptions.
* @return true if executed and completed normally, else false
*/
abstract boolean exec();
/**
* Sets status to indicate this task is done.
*/
final void setDone() {
_unsafe.putOrderedInt(this, statusOffset, COMPLETED);
}
final boolean casStatus(int cmp, int val) {
return cmp == status &&
_unsafe.compareAndSwapInt(this, statusOffset, cmp, val);
}
final void setStatus(int s) {
_unsafe.putOrderedInt(this, statusOffset, s);
}
final void incrementStatus() {
for (;;) {
int s = status; // force fail if already negative
if (s < 0 || _unsafe.compareAndSwapInt(this, statusOffset, s, s+1))
break;
}
}
/**
* Sets status to indicate exceptional completion. Note that we
* allow races across normal and exceptional completion.
*/
final void setDoneExceptionally(Throwable rex) {
setException(this, rex);
status = HAS_EXCEPTION;
}
/**
* Sets status to cancelled only if in initial state. Uses same
* implementation as cancel() but used internally to safely set
* status on pool shutdown etc even if cancel is overridden.
*/
final void setCancelled() {
_unsafe.compareAndSwapInt(this, statusOffset, 0, CANCELLED);
}
/**
* Workaround for not being able to rethrow unchecked exceptions.
*/
static final void rethrowException(Throwable ex) {
if (ex != null)
_unsafe.throwException(ex);
}
/**
* Version of setDoneExceptionally that screens argument
*/
final void checkedSetDoneExceptionally(Throwable rex) {
if (!(rex instanceof RuntimeException) && !(rex instanceof Error))
throw new IllegalArgumentException(rex);
setDoneExceptionally(rex);
}
/**
* Returns result or throws exception.
* Only call when isDone known to be true.
*/
final V reportAsForkJoinResult() {
int s = status;
if (s == CANCELLED)
throw new CancellationException();
if (s == HAS_EXCEPTION)
rethrowException(getException(this));
return rawResult();
}
/**
* Returns result or throws exception using j.u.c.Future conventions
* Only call when isDone known to be true.
*/
final V reportAsFutureResult() throws ExecutionException {
Throwable ex;
int s = status;
if (s == CANCELLED)
throw new CancellationException();
if (s == HAS_EXCEPTION && (ex = getException(this)) != null)
throw new ExecutionException(ex);
return rawResult();
}
// public methods
/**
* Arranges to asynchronously execute this task, which will later
* be directly or indirectly joined by the caller of this method.
* While it is not necessarily enforced, it is a usage error to
* fork a task more than once unless it has completed and been
* reinitialized. This method may be invoked only from within
* other ForkJoinTask computations. Attempts to invoke in other
* contexts result in exceptions or errors including
* ClassCastException.
*/
public final void fork() {
((ForkJoinWorkerThread)(Thread.currentThread())).pushTask(this);
}
/**
* Returns the result of the computation when it is ready.
* Monitoring note: Callers of this method need not block, but may
* instead assist in performing computations that may directly or
* indirectly cause the result to be ready.
* This method may be invoked only from within other ForkJoinTask
* computations. Attempts to invoke in other contexts result
* in exceptions or errors including ClassCastException.
*
* @return the computed result
* @throws Throwable (a RuntimeException, Error, or unchecked
* exception) if the underlying computation did so.
*/
public final V join() {
int s = status;
if (s >= 0) {
((ForkJoinWorkerThread)(Thread.currentThread())).helpJoinTask(this);
s = status;
}
return s < COMPLETED? reportAsForkJoinResult() : rawResult();
}
/**
* Equivalent in effect to the sequence <tt>fork(); join();</tt>
* but likely to be more efficient.
* @throws Throwable (a RuntimeException, Error, or unchecked
* exception) if the underlying computation did so.
* @return the computed result
*/
public V forkJoin() {
exec();
return join();
}
/**
* Returns true if the computation performed by this task has
* completed (or has been cancelled).
* @return true if this computation has completed
*/
public final boolean isDone() {
return status < 0;
}
/**
* Returns true if this task was cancelled.
* @return true if this task was cancelled
*/
public final boolean isCancelled() {
return status == CANCELLED;
}
/**
* Returns true if task currently has COMPLETED status. This
* method is not public because this fact may asynchronously
* change, which we can handle internally but not externally.
*/
final boolean completedNormally() {
return status == COMPLETED;
}
/**
* Returns true if task threw and exception or was cancelled
*/
final boolean completedAbnormally() {
return status < COMPLETED;
}
/**
* Asserts that the results of this task's computation will not be
* used. If a cancellation occurs before this task is processed,
* then its <tt>compute</tt> method will not be executed,
* <tt>isCancelled</tt> will report true, and <tt>join</tt> will
* result in a CancellationException being thrown. Otherwise,
* there are no guarantees about whether <tt>isCancelled</tt> will
* report true, whether <tt>join</tt> will return normally or via
* an exception, or whether these behaviors will remain consistent
* upon repeated invocation. This method may be overridden in
* subclasses, but if so, must still ensure that these minimal
* properties hold.
*
* <p> This method is designed to be invoked by <em>other</em>
* tasks. To abruptly terminate the current task, you should just
* return from its computation method, or <tt>throw new
* CancellationException()</tt>, or in AsyncActions, invoke
* <tt>finishExceptionally()</tt>.
*/
public void cancel() {
// Using 0 here succeeds if task never touched, and maybe otherwise
_unsafe.compareAndSwapInt(this, statusOffset, 0, CANCELLED);
}
/**
* Returns the exception thrown by method <tt>compute</tt>, or a
* CancellationException if cancelled, or null if none or if the
* method has not yet completed.
* @return the exception, or null if none
*/
public final Throwable getException() {
int s = status;
if (s >= COMPLETED)
return null;
if (s == CANCELLED)
return new CancellationException();
return getException(this);
}
/**
* Returns the result that would be returned by <tt>join</tt>, or
* null if this task is not known to have been completed. This
* method is designed to aid debugging, as well as to support
* extensions. Its use in any other context is strongly
* discouraged.
* @return the result, or null if not completed.
*/
public abstract V rawResult();
/**
* Resets the internal bookkeeping state of this task, allowing a
* subsequent <tt>fork</tt>. This method allows repeated reuse of
* this task, but only if reuse occurs when this task has either
* never been forked, or has been forked, then completed and all
* outstanding joins of this task have also completed. Effects
* under any other usage conditions are not guaranteed, and are
* almost surely wrong. This method may be useful when executing
* pre-constructed trees of subtasks in loops.
*/
public void reinitialize() {
if (status == HAS_EXCEPTION)
clearException(this);
status = 0;
}
/**
* Joins this task, 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. This method may be invoked only from
* within other ForkJoinTask computations. Attempts to invoke in
* other contexts result in exceptions or errors including
* ClassCastException.
*/
public final void quietlyJoin() {
((ForkJoinWorkerThread)(Thread.currentThread())).helpJoinTask(this);
}
// Serialization support
private static final long serialVersionUID = -7721805057305804111L;
/**
* Save the state to a stream.
*
* @serialData the current run status and the exception thrown
* during execution, or null if none.
* @param s the stream
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
s.defaultWriteObject();
s.writeObject(getException(this));
}
/**
* Reconstitute the instance from a stream.
* @param s the stream
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
Object ex = s.readObject();
if (ex != null)
setException(this, (Throwable)ex);
}
// Temporary Unsafe mechanics for preliminary release
static final Unsafe _unsafe;
static final long statusOffset;
static {
try {
if (ForkJoinWorkerThread.class.getClassLoader() != null) {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
_unsafe = (Unsafe)f.get(null);
}
else
_unsafe = Unsafe.getUnsafe();
statusOffset = _unsafe.objectFieldOffset
(ForkJoinTask.class.getDeclaredField("status"));
} catch (Exception ex) { throw new Error(ex); }
}
}