ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/jsr166y/forkjoin/LinkedAsyncAction.java
Revision: 1.8
Committed: Sat Sep 6 13:18:06 2008 UTC (15 years, 9 months ago) by dl
Branch: MAIN
Changes since 1.7: +41 -72 lines
Log Message:
Reduce overhead and improve scalability; refactor Async classes; Remove previous approach to cyclic tasks

File Contents

# Content
1 /*
2 * Written by Doug Lea with assistance from members of JCP JSR-166
3 * Expert Group and released to the public domain, as explained at
4 *
5 */
6
7 package jsr166y.forkjoin;
8 import java.util.*;
9 import java.util.concurrent.*;
10 import java.util.concurrent.atomic.*;
11
12 /**
13 * AsyncActions that may be linked in parent-child relationships.
14 *
15 * <p> Upon construction, an LinkedAsyncAction may register as a
16 * subtask of a given parent task. In this case, completion of this
17 * task will propagate to its parent. If the parent's pending subtask
18 * completion count becomes zero, it too will finish.
19 * LinkedAsyncActions rarely use methods <tt>join</tt> or
20 * <tt>invoke</tt> but instead propagate completion to parents
21 * implicitly via <tt>finish</tt>. While typical, it is not necessary
22 * for each task to <tt>finish</tt> itself. For example, it is
23 * possible to treat one subtask as a continuation of the current task
24 * by not registering it on construction. In this case, a
25 * <tt>finish</tt> of the subtask will trigger <tt>finish</tt> of the
26 * parent without the parent explicitly doing so.
27 *
28 * <p> In addition to supporting these different computation styles
29 * compared to Recursive tasks, LinkedAsyncActions may have smaller
30 * stack space footprints while executing, but may have greater
31 * per-task overhead.
32 *
33 * <p> <b>Sample Usage.</b> Here is a sketch of an LinkedAsyncAction
34 * that visits all of the nodes of a graph. The details of the graph's
35 * Node and Edge classes are omitted, but we assume each node contains
36 * an <tt>AtomicBoolean</tt> mark that starts out false. To execute
37 * this, you would create a GraphVisitor for the root node with null
38 * parent, and <tt>invoke</tt> in a ForkJoinPool. Upon return, all
39 * reachable nodes will have been visited.
40 *
41 * <pre>
42 * class GraphVisitor extends LinkedAsyncAction {
43 * final Node node;
44 * GraphVisitor(GraphVistor parent, Node node) {
45 * super(parent); this.node = node;
46 * }
47 * protected void compute() {
48 * if (node.mark.compareAndSet(false, true)) {
49 * for (Edge e : node.edges()) {
50 * Node dest = e.getDestination();
51 * if (!dest.mark.get())
52 * new GraphVisitor(this, dest).fork();
53 * }
54 * visit(node);
55 * }
56 * finish();
57 * }
58 * }
59 * </pre>
60 *
61 */
62 public abstract class LinkedAsyncAction extends AsyncAction {
63 /**
64 * Parent to notify on completion
65 */
66 private LinkedAsyncAction parent;
67
68 /*
69 * Note: we also piggyback pending join count on
70 * ForkJoinTask.status field.
71 */
72
73 /**
74 * Creates a new action with no parent. (You can add a parent
75 * later (but before forking) via <tt>reinitialize</tt>).
76 */
77 protected LinkedAsyncAction() {
78 }
79
80 /**
81 * Creates a new action with the given parent. If the parent is
82 * non-null, this tasks registers with the parent, in which case,
83 * the parent task cannot complete until this task completes.
84 * @param parent the parent task, or null if none
85 */
86 protected LinkedAsyncAction(LinkedAsyncAction parent) {
87 this.parent = parent;
88 if (parent != null)
89 parent.incrementStatus();
90 }
91
92 /**
93 * Creates a new action with the given parent, optionally
94 * registering with the parent. If the parent is non-null and
95 * <tt>register</tt> is true, this tasks registers with the
96 * parent, in which case, the parent task cannot complete until
97 * this task completes.
98 * @param parent the parent task, or null if none
99 * @param register true if parent must wait for this task
100 * to complete before it completes
101 */
102 protected LinkedAsyncAction(LinkedAsyncAction parent, boolean register) {
103 this.parent = parent;
104 if (parent != null && register)
105 parent.incrementStatus();
106 }
107
108 /**
109 * Creates a new action with the given parent, optionally
110 * registering with the parent, and setting the pending join count
111 * to the given value. If the parent is non-null and
112 * <tt>register</tt> is true, this tasks registers with the
113 * parent, in which case, the parent task cannot complete until
114 * this task completes. Setting the pending join count requires
115 * care -- it is correct only if child tasks do not themselves
116 * register.
117 * @param parent the parent task, or null if none
118 * @param register true if parent must wait for this task
119 * to complete before it completes
120 * @param pending the pending join count
121 */
122 protected LinkedAsyncAction(LinkedAsyncAction parent,
123 boolean register,
124 int pending) {
125 if (pending < 0)
126 throw new IllegalArgumentException();
127 this.parent = parent;
128 status = pending;
129 if (parent != null && register)
130 parent.incrementStatus();
131 }
132
133 /**
134 * Overridable callback action triggered by <tt>finish</tt>. Upon
135 * invocation, all subtasks have completed. After return, this
136 * task <tt>isDone</tt> and is joinable by other tasks. The
137 * default version of this method does nothing. But it may may be
138 * overridden in subclasses to perform some action when this task
139 * is about to complete.
140 */
141 protected void onCompletion() {
142 }
143
144 /**
145 * Overridable callback action triggered by
146 * <tt>finishExceptionally</tt>. Upon invocation, this task has
147 * aborted due to an exception (accessible via
148 * <tt>getException</tt>). If this method returns <tt>true</tt>,
149 * the exception propagates to the current task's
150 * parent. Otherwise, normal completion is propagated. The
151 * default version of this method does nothing and returns
152 * <tt>true</tt>.
153 * @return true if this task's exception should be propagated to
154 * this tasks parent.
155 */
156 protected boolean onException() {
157 return true;
158 }
159
160 /**
161 * Completes this task. If the pending subtask completion count is
162 * zero, invokes <tt>onCompletion</tt>, then causes this task to
163 * be joinable (<tt>isDone</tt> becomes true), and then
164 * recursively applies to this tasks's parent, if it exists. If an
165 * exception is encountered in any <tt>onCompletion</tt>
166 * invocation, that task and its ancestors
167 * <tt>finishExceptionally</tt>.
168 */
169 public final void finish() {
170 LinkedAsyncAction a = this;
171 while (a != null && !a.isDone()) {
172 int c = a.status;
173 if (c <= 0) {
174 try {
175 a.onCompletion();
176 } catch (Throwable rex) {
177 a.finishExceptionally(rex);
178 return;
179 }
180 a.setDone();
181 a = a.parent;
182 }
183 else if (a.casStatus(c, c-1))
184 return;
185 }
186 }
187
188 /**
189 * Completes this task abnormally. Unless this task already
190 * cancelled or aborted, upon invocation, this method invokes
191 * <tt>onException</tt>, and then, depending on its return value,
192 * finishes parent (if one exists) exceptionally or normally. To
193 * avoid unbounded exception loops, this method aborts if an
194 * exception is encountered in any <tt>onException</tt>
195 * invocation.
196 * @param ex the exception to throw when joining this task
197 * @throws NullPointerException if ex is null
198 * @throws Throwable if any invocation of
199 * <tt>onException</tt> does so.
200 */
201 public final void finishExceptionally(Throwable ex) {
202 if (!(ex instanceof RuntimeException) && !(ex instanceof Error))
203 throw new IllegalArgumentException(ex);
204 doFinishExceptionally(ex);
205 }
206
207 /**
208 * Internal version without argument screening
209 */
210 private void doFinishExceptionally(Throwable ex) {
211 LinkedAsyncAction a = this;
212 for (;;) {
213 if (a.status == ForkJoinTask.HAS_EXCEPTION)
214 break;
215 a.setDoneExceptionally(ex);
216 boolean up = a.onException(); // abort if this throws
217 a = a.parent;
218 if (a == null)
219 break;
220 if (!up) {
221 a.finish();
222 break;
223 }
224 }
225 }
226
227 /**
228 * Returns this task's parent, or null if none.
229 * @return this task's parent, or null if none.
230 */
231 public final LinkedAsyncAction getParent() {
232 return parent;
233 }
234
235 /**
236 * Returns the number of subtasks that have not yet completed.
237 * @return the number of subtasks that have not yet completed.
238 */
239 public final int getPendingSubtaskCount() {
240 return status;
241 }
242
243 /**
244 * Resets the internal bookkeeping state of this task, maintaining
245 * the current parent but clearing pending joins.
246 */
247 public void reinitialize() {
248 super.reinitialize();
249 }
250
251 /**
252 * Resets the internal bookkeeping state of this task, maintaining
253 * the current parent and setting pending joins to the given value.
254 * @param pending the number of pending joins
255 */
256 public void reinitialize(int pending) {
257 if (pending < 0)
258 throw new IllegalArgumentException();
259 super.reinitialize();
260 status = pending;
261 }
262
263 /**
264 * Reinitialize with the given parent, optionally registering.
265 * @param parent the parent task, or null if none
266 * @param register true if parent must wait for this task
267 * to complete before it completes
268 */
269 public void reinitialize(LinkedAsyncAction parent, boolean register) {
270 super.reinitialize();
271 this.parent = parent;
272 if (parent != null && register)
273 parent.incrementStatus();
274 }
275
276 /**
277 * Reinitialize with the given parent, optionally registering
278 * and setting pending join count.
279 * @param parent the parent task, or null if none
280 * @param register true if parent must wait for this task
281 * to complete before it completes
282 * @param pending the pending join count
283 */
284 public void reinitialize(LinkedAsyncAction parent,
285 boolean register,
286 int pending) {
287 if (pending < 0)
288 throw new IllegalArgumentException();
289 super.reinitialize();
290 status = pending;
291 this.parent = parent;
292 if (parent != null && register)
293 parent.incrementStatus();
294 }
295
296 final boolean exec() {
297 if (status >= 0) {
298 try {
299 compute();
300 } catch(Throwable rex) {
301 doFinishExceptionally(rex);
302 }
303 }
304 return false;
305 }
306 }