ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/loops/LinkedAsyncAction.java
Revision: 1.15
Committed: Thu Jan 15 18:42:39 2015 UTC (9 years, 3 months ago) by jsr166
Branch: MAIN
CVS Tags: HEAD
Changes since 1.14: +1 -1 lines
Log Message:
typos

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