/* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at * */ package jsr166y.forkjoin; /** * AsyncActions that are always linked in binary parent-child * relationships. Compared to Recursive tasks, BinaryAsyncActions may * have smaller stack space footprints and faster completion mechanics * but higher per-task footprints. Compared to LinkedAsyncActions, * BinaryAsyncActions are simpler to use and have less overhead in * typical uasges but are restricted to binary computation trees. * * <p> Upon construction, an BinaryAsyncAction does not bear any * linkages. For non-root tasks, links must be established using * method <tt>linkSubtasks</tt> before use. * * <p> <b>Sample Usage.</b> A version of Fibonacci: * <pre> * class Fib extends BinaryAsyncAction { * final int n; * int result; * Fib(int n) { this.n = n; } * protected void compute() { * if (n > 1) { * linkAndForkSubtasks(new Fib(n-1), new Fib(n-2)); * else { * result = n; // fib(0)==0; fib(1)==1 * finish(); * } * } * protected void onFinish(BinaryAsyncAction x, BinaryAsyncAction y) { * result = ((Fib)x).result + ((Fib)y).result; * } * } * </pre> * An alternative, and usually faster strategy is to instead use a * loop to fork subtasks: * <pre> * protected void compute() { * Fib f = this; * while (f.n > 1) { * Fib left = new Fib(f.n - 1); * Fib right = new Fib(f.n - 2); * f.linkSubtasks(left, right); * right.fork(); // fork right * f = left; // loop on left * } * f.result = f.n; * f.finish(); * } * } * </pre> */ public abstract class BinaryAsyncAction extends AsyncAction { /** * Parent to propagate completion; nulled after completion to * avoid retaining entire tree as garbage */ private BinaryAsyncAction parent; /** * Sibling to access on subtask joins, also nulled after completion. */ private BinaryAsyncAction sibling; /* * Note: we also piggyback one bit join count on * ForkJoinTask.status field. The first arriving thread CAS's * status from 0 to 1. The second ultimately sets to negative * value to signify completion. */ /** * Creates a new action. Unless this is a root task, you will need * to link it using method <tt>linkSubtasks</tt> before forking as * a subtask. */ protected BinaryAsyncAction() { } /** * Establishes links for the given tasks to have the current task * as parent, and each other as siblings. * @param x one subtask * @param y the other subtask * @throws NullPointerException if either argument is null. */ public final void linkSubtasks(BinaryAsyncAction x, BinaryAsyncAction y) { x.parent = y.parent = this; x.sibling = y; y.sibling = x; } /** * Overridable callback action triggered upon <tt>finish</tt> of * subtasks. Upon invocation, both subtasks have completed. * After return, this task <tt>isDone</tt> and is joinable by * other tasks. The default version of this method does * nothing. But it may may be overridden in subclasses to perform * some action (for example a reduction) when this task is * completes. * @param x one subtask * @param y the other subtask */ protected void onFinish(BinaryAsyncAction x, BinaryAsyncAction y) { } /** * Overridable callback action triggered by * <tt>finishExceptionally</tt>. Upon invocation, this task has * aborted due to an exception (accessible via * <tt>getException</tt>). If this method returns <tt>true</tt>, * the exception propagates to the current task's * parent. Otherwise, normal completion is propagated. The * default version of this method does nothing and returns * <tt>true</tt>. * @return true if this task's exception should be propagated to * this tasks parent. */ protected boolean onException() { return true; } /** * Equivalent in effect to invoking <tt>linkSubtasks</tt> and then * forking both tasks. * @param x one subtask * @param y the other subtask */ public void linkAndForkSubtasks(BinaryAsyncAction x, BinaryAsyncAction y) { linkSubtasks(x, y); y.fork(); x.fork(); } /** * Completes this task, and if this task has a sibling that is * also complete, invokes <tt>onFinish</tt> of parent task, and so * on. If an exception is encountered, tasks instead * <tt>finishExceptionally</tt>. */ public final void finish() { // todo: Use removeIfNextLocalTask(s) without possibly blowing stack BinaryAsyncAction a = this; for (;;) { BinaryAsyncAction s = a.sibling; BinaryAsyncAction p = a.parent; a.sibling = null; a.parent = null; a.setDone(); if (p == null || p.casStatus(0, 1)) break; try { p.onFinish(a, s); } catch(Throwable rex) { p.doFinishExceptionally(rex); return; } a = p; } } /** * Completes this task abnormally. Unless this task already * cancelled or aborted, upon invocation, this method invokes * <tt>onException</tt>, and then, depending on its return value, * finishes parent (if one exists) exceptionally or normally. To * avoid unbounded exception loops, this method aborts if an * exception is encountered in any <tt>onException</tt> * invocation. * @param ex the exception to throw when joining this task * @throws NullPointerException if ex is null * @throws Throwable if any invocation of * <tt>onException</tt> does so. */ public final void finishExceptionally(Throwable ex) { if (!(ex instanceof RuntimeException) && !(ex instanceof Error)) throw new IllegalArgumentException(ex); doFinishExceptionally(ex); } /** * Internal version without argument screening */ private void doFinishExceptionally(Throwable ex) { BinaryAsyncAction a = this; while (a.status != ForkJoinTask.HAS_EXCEPTION) { a.setDoneExceptionally(ex); BinaryAsyncAction s = a.sibling; if (s != null) s.cancel(); if (!a.onException() || (a = a.parent) == null) break; } } /** * Returns this task's parent, or null if none or this task * is already finished. * @return this task's parent, or null if none. */ public final BinaryAsyncAction getParent() { return parent; } /** * Returns this task's sibling, or null if none or this task is * already finished. * @return this task's sibling, or null if none. */ public BinaryAsyncAction getSibling() { return sibling; } /** * Resets the internal bookkeeping state of this task, erasing * parent and child linkages. */ public void reinitialize() { parent = sibling = null; super.reinitialize(); } final boolean exec() { if (status >= 0) { try { compute(); } catch(Throwable rex) { doFinishExceptionally(rex); } } return false; } }