ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/jsr166y/CountedCompleter.java
(Generate patch)

Comparing jsr166/src/jsr166y/CountedCompleter.java (file contents):
Revision 1.14 by dl, Mon Nov 19 18:12:42 2012 UTC vs.
Revision 1.15 by dl, Fri Nov 23 17:50:59 2012 UTC

# Line 10 | Line 10 | package jsr166y;
10   * A {@link ForkJoinTask} with a completion action performed when
11   * triggered and there are no remaining pending
12   * actions. CountedCompleters are in general more robust in the
13 < * presence of subtask stalls and blockage than are other forms for
14 < * ForkJoinTasks, but are in general less intuitive to program.  Uses
15 < * of CountedCompleter are similar to those of other completion based
13 > * presence of subtask stalls and blockage than are other forms of
14 > * ForkJoinTasks, but are less intuitive to program.  Uses of
15 > * CountedCompleter are similar to those of other completion based
16   * components (such as {@link java.nio.channels.CompletionHandler})
17   * except that multiple <em>pending</em> completions may be necessary
18   * to trigger the {@link #onCompletion} action, not just one. Unless
# Line 30 | Line 30 | package jsr166y;
30   * internal bookkeeping. In particular, the identities of pending
31   * tasks are not maintained. As illustrated below, you can create
32   * subclasses that do record some or all pending tasks or their
33 < * results when needed. Because CountedCompleters provide only basic
33 > * results when needed.  As illustrated below, utility methods
34 > * supporting customization of completion traversals are also
35 > * provided. However, because CountedCompleters provide only basic
36   * synchronization mechanisms, it may be useful to create further
37 < * abstract subclasses that maintain linkages and fields and support
38 < * methods appropriate for a set of related usages.
37 > * abstract subclasses that maintain linkages, fields, and additional
38 > * support methods appropriate for a set of related usages.
39   *
40   * <p>A concrete CountedCompleter class must define method {@link
41   * #compute}, that should in most cases (as illustrated below), invoke
# Line 51 | Line 53 | package jsr166y;
53   * function of one or more fields) of the CountedCompleter object that
54   * holds the result upon completion. Method {@link #setRawResult} by
55   * default plays no role in CountedCompleters.  It is possible, but
56 < * not usually applicable, to override this method to maintain other
56 > * rarely applicable, to override this method to maintain other
57   * objects or fields holding result data.
58   *
59   * <p>A CountedCompleter that does not itself have a completer (i.e.,
# Line 103 | Line 105 | package jsr166y;
105   *
106   * class ForEach<E> extends CountedCompleter<Void> {
107   *
108 < *     public static <E> void forEach(E[] array, MyOperation<E> op) {
109 < *         new ForEach<E>(null, array, op, 0, array.length).invoke();
110 < *     }
111 < *
112 < *     final E[] array; final MyOperation<E> op; final int lo, hi;
113 < *     ForEach(CountedCompleter<?> p, E[] array, MyOperation<E> op, int lo, int hi) {
114 < *         super(p);
115 < *         this.array = array; this.op = op; this.lo = lo; this.hi = hi;
116 < *     }
117 < *
118 < *     public void compute() { // version 1
119 < *         if (hi - lo >= 2) {
120 < *             int mid = (lo + hi) >>> 1;
121 < *             setPendingCount(2); // must set pending count before fork
122 < *             new ForEach(this, array, op, mid, hi).fork(); // right child
123 < *             new ForEach(this, array, op, lo, mid).fork(); // left child
124 < *         }
125 < *         else if (hi > lo)
126 < *             op.apply(array[lo]);
127 < *         tryComplete();
128 < *     }
108 > *   public static <E> void forEach(E[] array, MyOperation<E> op) {
109 > *     new ForEach<E>(null, array, op, 0, array.length).invoke();
110 > *   }
111 > *
112 > *   final E[] array; final MyOperation<E> op; final int lo, hi;
113 > *   ForEach(CountedCompleter<?> p, E[] array, MyOperation<E> op, int lo, int hi) {
114 > *     super(p);
115 > *     this.array = array; this.op = op; this.lo = lo; this.hi = hi;
116 > *   }
117 > *
118 > *   public void compute() { // version 1
119 > *     if (hi - lo >= 2) {
120 > *       int mid = (lo + hi) >>> 1;
121 > *       setPendingCount(2); // must set pending count before fork
122 > *       new ForEach(this, array, op, mid, hi).fork(); // right child
123 > *       new ForEach(this, array, op, lo, mid).fork(); // left child
124 > *     }
125 > *     else if (hi > lo)
126 > *       op.apply(array[lo]);
127 > *     tryComplete();
128 > *   }
129   * } }</pre>
130   *
131   * This design can be improved by noticing that in the recursive case,
# Line 135 | Line 137 | package jsr166y;
137   *
138   * <pre> {@code
139   * class ForEach<E> ...
140 < *     public void compute() { // version 2
141 < *         if (hi - lo >= 2) {
142 < *             int mid = (lo + hi) >>> 1;
143 < *             setPendingCount(1); // only one pending
144 < *             new ForEach(this, array, op, mid, hi).fork(); // right child
145 < *             new ForEach(this, array, op, lo, mid).compute(); // direct invoke
146 < *         }
147 < *         else {
148 < *             if (hi > lo)
149 < *                 op.apply(array[lo]);
150 < *             tryComplete();
149 < *         }
140 > *   public void compute() { // version 2
141 > *     if (hi - lo >= 2) {
142 > *       int mid = (lo + hi) >>> 1;
143 > *       setPendingCount(1); // only one pending
144 > *       new ForEach(this, array, op, mid, hi).fork(); // right child
145 > *       new ForEach(this, array, op, lo, mid).compute(); // direct invoke
146 > *     }
147 > *     else {
148 > *       if (hi > lo)
149 > *         op.apply(array[lo]);
150 > *       tryComplete();
151   *     }
152 + *   }
153   * }</pre>
154   *
155   * As a further improvement, notice that the left task need not even
156   * exist.  Instead of creating a new one, we can iterate using the
157 < * original task, and add a pending count for each fork.
157 > * original task, and add a pending count for each fork. Additionally,
158 > * because no task in this tree implements an {@link #onCompletion}
159 > * method, {@code tryComplete()} can be replaced with {@link
160 > * #propagateCompletion}.
161   *
162   * <pre> {@code
163   * class ForEach<E> ...
164 < *     public void compute() { // version 3
165 < *         int l = lo,  h = hi;
166 < *         while (h - l >= 2) {
167 < *             int mid = (l + h) >>> 1;
168 < *             addToPendingCount(1);
169 < *             new ForEach(this, array, op, mid, h).fork(); // right child
170 < *             h = mid;
171 < *         }
172 < *         if (h > l)
173 < *             op.apply(array[l]);
174 < *         tryComplete();
175 < *     }
164 > *   public void compute() { // version 3
165 > *     int l = lo,  h = hi;
166 > *     while (h - l >= 2) {
167 > *       int mid = (l + h) >>> 1;
168 > *       addToPendingCount(1);
169 > *       new ForEach(this, array, op, mid, h).fork(); // right child
170 > *       h = mid;
171 > *     }
172 > *     if (h > l)
173 > *       op.apply(array[l]);
174 > *     propagateCompletion();
175 > *   }
176   * }</pre>
177   *
178   * Additional improvements of such classes might entail precomputing
# Line 176 | Line 181 | package jsr166y;
181   * instead of two per iteration, and using an adaptive threshold
182   * instead of always subdividing down to single elements.
183   *
184 + * <p><b>Searching.</b> A tree of CountedCompleters can search for a
185 + * value or property in different parts of a data structure, and
186 + * report a result in an {@link java.util.concurrent.AtomicReference}
187 + * as soon as one is found. The others can poll the result to avoid
188 + * unnecessary work. (You could additionally {@link #cancel} other
189 + * tasks, but it is usually simpler and more efficient to just let
190 + * them notice that the result is set and if so skip further
191 + * processing.)  Illustrating again with an array using full
192 + * partitioning (again, in practice, leaf tasks will almost always
193 + * process more than one element):
194 + *
195 + * <pre> {@code
196 + * class Searcher<E> extends CountedCompleter<E> {
197 + *   final E[] array; final AtomicReference<E> result; final int lo, hi;
198 + *   Searcher(CountedCompleter<?> p, E[] array, AtomicReference<E> result, int lo, int hi) {
199 + *     super(p);
200 + *     this.array = array; this.result = result; this.lo = lo; this.hi = hi;
201 + *   }
202 + *   public E getRawResult() { return result.get(); }
203 + *   public void compute() { // similar to ForEach version 3
204 + *     int l = lo,  h = hi;
205 + *     while (result.get() == null && h >= l) {
206 + *       if (h - l >= 2) {
207 + *         int mid = (l + h) >>> 1;
208 + *         addToPendingCount(1);
209 + *         new Searcher(this, array, result, mid, h).fork();
210 + *         h = mid;
211 + *       }
212 + *       else {
213 + *         E x = array[l];
214 + *         if (matches(x) && result.compareAndSet(null, x))
215 + *           quietlyCompleteRoot(); // root task is now joinable
216 + *         break;
217 + *       }
218 + *     }
219 + *     tryComplete(); // normally complete whether or not found
220 + *   }
221 + *   boolean matches(E e) { ... } // return true if found
222 + *
223 + *   public static <E> E search(E[] array) {
224 + *       return new Searcher<E>(null, array, new AtomicReference<E>(), 0, array.length).invoke();
225 + *   }
226 + *}}</pre>
227 + *
228 + * In this example, as well as others in which tasks have no other
229 + * effects except to compareAndSet a common result, the trailing
230 + * unconditional invocation of {@code tryComplete} could be made
231 + * conditional ({@code if (result.get() == null) tryComplete();})
232 + * because no further bookkeeping is required to manage completions
233 + * once the root task completes.
234 + *
235   * <p><b>Recording subtasks.</b> CountedCompleter tasks that combine
236   * results of multiple subtasks usually need to access these results
237   * in method {@link #onCompletion}. As illustrated in the following
# Line 192 | Line 248 | package jsr166y;
248   * class MyMapper<E> { E apply(E v) {  ...  } }
249   * class MyReducer<E> { E apply(E x, E y) {  ...  } }
250   * class MapReducer<E> extends CountedCompleter<E> {
251 < *     final E[] array; final MyMapper<E> mapper;
252 < *     final MyReducer<E> reducer; final int lo, hi;
253 < *     MapReducer<E> sibling;
254 < *     E result;
255 < *     MapReducer(CountedCompleter p, E[] array, MyMapper<E> mapper,
256 < *                MyReducer<E> reducer, int lo, int hi) {
257 < *         super(p);
258 < *         this.array = array; this.mapper = mapper;
259 < *         this.reducer = reducer; this.lo = lo; this.hi = hi;
260 < *     }
261 < *     public void compute() {
262 < *         if (hi - lo >= 2) {
263 < *             int mid = (lo + hi) >>> 1;
264 < *             MapReducer<E> left = new MapReducer(this, array, mapper, reducer, lo, mid);
265 < *             MapReducer<E> right = new MapReducer(this, array, mapper, reducer, mid, hi);
266 < *             left.sibling = right;
267 < *             right.sibling = left;
268 < *             setPendingCount(1); // only right is pending
269 < *             right.fork();
270 < *             left.compute();     // directly execute left
271 < *         }
272 < *         else {
273 < *             if (hi > lo)
274 < *                 result = mapper.apply(array[lo]);
275 < *             tryComplete();
276 < *         }
277 < *     }
278 < *     public void onCompletion(CountedCompleter caller) {
279 < *         if (caller != this) {
280 < *            MapReducer<E> child = (MapReducer<E>)caller;
281 < *            MapReducer<E> sib = child.sibling;
282 < *            if (sib == null || sib.result == null)
283 < *                result = child.result;
284 < *            else
285 < *                result = reducer.apply(child.result, sib.result);
286 < *         }
287 < *     }
288 < *     public E getRawResult() { return result; }
289 < *
290 < *     public static <E> E mapReduce(E[] array, MyMapper<E> mapper, MyReducer<E> reducer) {
291 < *         return new MapReducer<E>(null, array, mapper, reducer,
292 < *                                  0, array.length).invoke();
293 < *     }
251 > *   final E[] array; final MyMapper<E> mapper;
252 > *   final MyReducer<E> reducer; final int lo, hi;
253 > *   MapReducer<E> sibling;
254 > *   E result;
255 > *   MapReducer(CountedCompleter<?> p, E[] array, MyMapper<E> mapper,
256 > *              MyReducer<E> reducer, int lo, int hi) {
257 > *     super(p);
258 > *     this.array = array; this.mapper = mapper;
259 > *     this.reducer = reducer; this.lo = lo; this.hi = hi;
260 > *   }
261 > *   public void compute() {
262 > *     if (hi - lo >= 2) {
263 > *       int mid = (lo + hi) >>> 1;
264 > *       MapReducer<E> left = new MapReducer(this, array, mapper, reducer, lo, mid);
265 > *       MapReducer<E> right = new MapReducer(this, array, mapper, reducer, mid, hi);
266 > *       left.sibling = right;
267 > *       right.sibling = left;
268 > *       setPendingCount(1); // only right is pending
269 > *       right.fork();
270 > *       left.compute();     // directly execute left
271 > *     }
272 > *     else {
273 > *       if (hi > lo)
274 > *           result = mapper.apply(array[lo]);
275 > *       tryComplete();
276 > *     }
277 > *   }
278 > *   public void onCompletion(CountedCompleter<?> caller) {
279 > *     if (caller != this) {
280 > *      MapReducer<E> child = (MapReducer<E>)caller;
281 > *      MapReducer<E> sib = child.sibling;
282 > *      if (sib == null || sib.result == null)
283 > *        result = child.result;
284 > *      else
285 > *        result = reducer.apply(child.result, sib.result);
286 > *     }
287 > *   }
288 > *   public E getRawResult() { return result; }
289 > *
290 > *   public static <E> E mapReduce(E[] array, MyMapper<E> mapper, MyReducer<E> reducer) {
291 > *     return new MapReducer<E>(null, array, mapper, reducer,
292 > *                              0, array.length).invoke();
293 > *   }
294   * } }</pre>
295   *
296   * Here, method {@code onCompletion} takes a form common to many
# Line 253 | Line 309 | package jsr166y;
309   * within this method to ensure thread safety of accesses to fields of
310   * this task or other completed tasks.
311   *
312 < * <p><b>Searching.</b> A tree of CountedCompleters can search for a
313 < * value or property in different parts of a data structure, and
314 < * report a result in an {@link java.util.concurrent.AtomicReference}
315 < * as soon as one is found. The others can poll the result to avoid
316 < * unnecessary work. (You could additionally {@link #cancel} other
317 < * tasks, but it is usually simpler and more efficient to just let
318 < * them notice that the result is set and if so skip further
263 < * processing.)  Illustrating again with an array using full
264 < * partitioning (again, in practice, leaf tasks will almost always
265 < * process more than one element):
312 > * <p><b>Completion Traversals</b>. If using {@code onCompletion} to
313 > * process completions is inapplicable or inconvenient, you can use
314 > * methods {@link #firstComplete} and {@link #nextComplete} to create
315 > * custom traversals.  For example, to define a MapReducer that only
316 > * splits out right-hand tasks in the form of the third ForEach
317 > * example, the completions must cooperatively reduce along
318 > * unexhausted subtask links, which can be done as follows:
319   *
320   * <pre> {@code
321 < * class Searcher<E> extends CountedCompleter<E> {
322 < *     final E[] array; final AtomicReference<E> result; final int lo, hi;
323 < *     Searcher(CountedCompleter<?> p, E[] array, AtomicReference<E> result, int lo, int hi) {
324 < *         super(p);
325 < *         this.array = array; this.result = result; this.lo = lo; this.hi = hi;
326 < *     }
327 < *     public E getRawResult() { return result.get(); }
328 < *     public void compute() { // similar to ForEach version 3
329 < *         int l = lo,  h = hi;
330 < *         while (h - l >= 2 && result.get() == null) {
331 < *             int mid = (l + h) >>> 1;
332 < *             addToPendingCount(1);
333 < *             new Searcher(this, array, result, mid, h).fork();
334 < *             h = mid;
335 < *         }
336 < *         if (h > l && result.get() == null && matches(array[l]) &&
337 < *             result.compareAndSet(null, array[l]))
338 < *             getRoot().quietlyComplete(); // root task is now joinable
339 < *
340 < *         tryComplete(); // normally complete whether or not found
341 < *     }
342 < *     boolean matches(E e) { ... } // return true if found
343 < *
344 < *     public static <E> E search(E[] array) {
345 < *         return new Searcher<E>(null, array, new AtomicReference<E>(), 0, array.length).invoke();
346 < *     }
347 < *}}</pre>
348 < *
349 < * In this example, as well as others in which tasks have no other
350 < * effects except to compareAndSet a common result, the trailing
351 < * unconditional invocation of {@code tryComplete} could be made
352 < * conditional ({@code if (result.get() == null) tryComplete();})
353 < * because no further bookkeeping is required to manage completions
354 < * once the root task completes.
321 > * class MapReducer<E> extends CountedCompleter<E> { // version 2
322 > *   final E[] array; final MyMapper<E> mapper;
323 > *   final MyReducer<E> reducer; final int lo, hi;
324 > *   MapReducer<E> forks, next; // record subtask forks in list
325 > *   E result;
326 > *   MapReducer(CountedCompleter<?> p, E[] array, MyMapper<E> mapper,
327 > *              MyReducer<E> reducer, int lo, int hi, MapReducer<E> next) {
328 > *     super(p);
329 > *     this.array = array; this.mapper = mapper;
330 > *     this.reducer = reducer; this.lo = lo; this.hi = hi;
331 > *     this.next = next;
332 > *   }
333 > *   public void compute() {
334 > *     int l = lo,  h = hi;
335 > *     while (h - l >= 2) {
336 > *       int mid = (l + h) >>> 1;
337 > *       addToPendingCount(1);
338 > *       (forks = new MapReducer(this, array, mapper, reducer, mid, h, forks)).fork;
339 > *       h = mid;
340 > *     }
341 > *     if (h > l)
342 > *       result = mapper.apply(array[l]);
343 > *     // process completions by reducing along and advancing subtask links
344 > *     for (CountedCompleter<?> c = firstComplete(); c != null; c = c.nextComplete()) {
345 > *       for (MapReducer t = (MapReducer)c, s = t.forks;  s != null; s = t.forks = s.next)
346 > *         t.result = reducer.apply(t.result, s.result);
347 > *     }
348 > *   }
349 > *   public E getRawResult() { return result; }
350 > *
351 > *   public static <E> E mapReduce(E[] array, MyMapper<E> mapper, MyReducer<E> reducer) {
352 > *     return new MapReducer<E>(null, array, mapper, reducer,
353 > *                              0, array.length, null).invoke();
354 > *   }
355 > * }}</pre>
356   *
357   * <p><b>Triggers.</b> Some CountedCompleters are themselves never
358   * forked, but instead serve as bits of plumbing in other designs;
# Line 309 | Line 363 | package jsr166y;
363   * class HeaderBuilder extends CountedCompleter<...> { ... }
364   * class BodyBuilder extends CountedCompleter<...> { ... }
365   * class PacketSender extends CountedCompleter<...> {
366 < *     PacketSender(...) { super(null, 1); ... } // trigger on second completion
367 < *     public void compute() { } // never called
368 < *     public void onCompletion(CountedCompleter<?> caller) { sendPacket(); }
366 > *   PacketSender(...) { super(null, 1); ... } // trigger on second completion
367 > *   public void compute() { } // never called
368 > *   public void onCompletion(CountedCompleter<?> caller) { sendPacket(); }
369   * }
370   * // sample use:
371   * PacketSender p = new PacketSender();
# Line 446 | Line 500 | public abstract class CountedCompleter<T
500       *
501       * @param expected the expected value
502       * @param count the new value
503 <     * @return true is successful
503 >     * @return true if successful
504       */
505      public final boolean compareAndSetPendingCount(int expected, int count) {
506          return U.compareAndSwapInt(this, PENDING, expected, count);
507      }
508  
509      /**
510 +     * If the pending count is nonzero, (atomically) decrements it.
511 +     *
512 +     * @return the initial (undecremented) pending count holding on entry
513 +     * to this method
514 +     */
515 +    public final int decrementPendingCountUnlessZero() {
516 +        int c;
517 +        do {} while ((c = pending) != 0 &&
518 +                     !U.compareAndSwapInt(this, PENDING, c, c - 1));
519 +        return c;
520 +    }
521 +
522 +    /**
523       * Returns the root of the current computation; i.e., this
524       * task if it has no completer, else its completer's root.
525       *
# Line 487 | Line 554 | public abstract class CountedCompleter<T
554      }
555  
556      /**
557 +     * Equivalent to {@link #tryComplete} but does not invoke {@link
558 +     * #onCompletion} along the completion path: If the pending count
559 +     * is nonzero, decrements the count; otherwise, similarly tries to
560 +     * complete this task's completer, if one exists, else marks this
561 +     * task as complete. This method may be useful in cases where
562 +     * {@code onCompletion} should not, or need not, be invoked for
563 +     * each completer in a computation.
564 +     */
565 +    public final void propagateCompletion() {
566 +        CountedCompleter<?> a = this, s = a;
567 +        for (int c;;) {
568 +            if ((c = a.pending) == 0) {
569 +                if ((a = (s = a).completer) == null) {
570 +                    s.quietlyComplete();
571 +                    return;
572 +                }
573 +            }
574 +            else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
575 +                return;
576 +        }
577 +    }
578 +
579 +    /**
580       * Regardless of pending count, invokes {@link #onCompletion},
581       * marks this task as complete and further triggers {@link
582       * #tryComplete} on this task's completer, if one exists.  The
# Line 499 | Line 589 | public abstract class CountedCompleter<T
589       * any one (versus all) of several subtask results are obtained.
590       * However, in the common (and recommended) case in which {@code
591       * setRawResult} is not overridden, this effect can be obtained
592 <     * more simply using {@code getRoot().quietlyComplete();}.
592 >     * more simply using {@code quietlyCompleteRoot();}.
593       *
594       * @param rawResult the raw result
595       */
# Line 512 | Line 602 | public abstract class CountedCompleter<T
602              p.tryComplete();
603      }
604  
605 +
606 +    /**
607 +     * If this task's pending count is zero, returns this task;
608 +     * otherwise decrements its pending count and returns {@code
609 +     * null}. This method is designed to be used with {@link
610 +     * #nextComplete} in completion traversal loops.
611 +     *
612 +     * @return this task, if pending count was zero, else {@code null}
613 +     */
614 +    public final CountedCompleter<?> firstComplete() {
615 +        for (int c;;) {
616 +            if ((c = pending) == 0)
617 +                return this;
618 +            else if (U.compareAndSwapInt(this, PENDING, c, c - 1))
619 +                return null;
620 +        }
621 +    }
622 +
623 +    /**
624 +     * If this task does not have a completer, invokes {@link
625 +     * ForkJoinTask#quietlyComplete} and returns {@code null}.  Or, if
626 +     * this task's pending count is non-zero, decrements its pending
627 +     * count and returns {@code null}.  Otherwise, returns the
628 +     * completer.  This method can be used as part of a completion
629 +     * traversal loop for homogenous task hierarchies:
630 +     *
631 +     * <pre> {@code
632 +     * for(CountedCompleter<?> c = firstComplete(); c != null; c = c.nextComplete()) {
633 +     *   // ... process c ...
634 +     * }}</pre>
635 +     *
636 +     * @return the completer, or {@code null} if none
637 +     */
638 +    public final CountedCompleter<?> nextComplete() {
639 +        CountedCompleter<?> p;
640 +        if ((p = completer) != null)
641 +            return p.firstComplete();
642 +        else {
643 +            quietlyComplete();
644 +            return null;
645 +        }
646 +    }
647 +
648 +    /**
649 +     * Equivalent to {@code getRoot().quietlyComplete()}.
650 +     */
651 +    public final void quietlyCompleteRoot() {
652 +        for (CountedCompleter<?> a = this, p;;) {
653 +            if ((p = a.completer) == null) {
654 +                a.quietlyComplete();
655 +                return;
656 +            }
657 +            a = p;
658 +        }
659 +    }
660 +
661      /**
662       * Support for FJT exception propagation
663       */
# Line 533 | Line 679 | public abstract class CountedCompleter<T
679      /**
680       * Returns the result of the computation. By default
681       * returns {@code null}, which is appropriate for {@code Void}
682 <     * actions, but in other cases should be overridden.
682 >     * actions, but in other cases should be overridden, almost
683 >     * always to return a field or function of a field that
684 >     * holds the result upon completion.
685       *
686       * @return the result of the computation
687       */
# Line 542 | Line 690 | public abstract class CountedCompleter<T
690      /**
691       * A method that result-bearing CountedCompleters may optionally
692       * use to help maintain result data.  By default, does nothing.
693 <     * If this method is overridden to update existing objects or
694 <     * fields, then it must in general be defined to be thread-safe.
693 >     * Overrides are not recommended. However, if this method is
694 >     * overridden to update existing objects or fields, then it must
695 >     * in general be defined to be thread-safe.
696       */
697      protected void setRawResult(T t) { }
698  

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines