1 //
2 // ========================================================================
3 // Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
8 //
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
11 //
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
14 //
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
17 //
18
19 package org.eclipse.jetty.util;
20
21 import java.util.concurrent.atomic.AtomicBoolean;
22
23
24 /* ------------------------------------------------------------ */
25 /** Iterating Callback.
26 * <p>This specialised callback is used when breaking up an
27 * asynchronous task into smaller asynchronous tasks. A typical pattern
28 * is that a successful callback is used to schedule the next sub task, but
29 * if that task completes quickly and uses the calling thread to callback
30 * the success notification, this can result in a growing stack depth.
31 * </p>
32 * <p>To avoid this issue, this callback uses an Atomicboolean to note
33 * if the success callback has been called during the processing of a
34 * sub task, and if so then the processing iterates rather than recurses.
35 * </p>
36 * <p>This callback is passed to the asynchronous handling of each sub
37 * task and a call the {@link #succeeded()} on this call back represents
38 * completion of the subtask. Only once all the subtasks are completed is
39 * the {@link Callback#succeeded()} method called on the {@link Callback} instance
40 * passed the the {@link #IteratingCallback(Callback)} constructor.</p>
41 *
42 */
43 public abstract class IteratingCallback implements Callback
44 {
45 final AtomicBoolean _iterating = new AtomicBoolean();
46 final Callback _callback;
47
48
49 public IteratingCallback(Callback callback)
50 {
51 _callback=callback;
52 }
53
54 /* ------------------------------------------------------------ */
55 /**
56 * Process a subtask.
57 * <p>Called by {@link #iterate()} to process a sub task of the overall task
58 * <p>
59 * @return True if the total task is complete. If false is returned
60 * then this Callback must be scheduled to receive either a call to
61 * {@link #succeeded()} or {@link #failed(Throwable)}.
62 * @throws Exception
63 */
64 abstract protected boolean process() throws Exception;
65
66 /* ------------------------------------------------------------ */
67 /** This method is called initially to start processing and
68 * is then called by subsequent sub task success to continue
69 * processing.
70 */
71 public void iterate()
72 {
73 try
74 {
75 // Keep iterating as long as succeeded() is called during process()
76 while(_iterating.compareAndSet(false,true))
77 {
78 // process and test if we are complete
79 if (process())
80 {
81 _callback.succeeded();
82 return;
83 }
84 }
85 }
86 catch(Exception e)
87 {
88 _iterating.set(false);
89 _callback.failed(e);
90 }
91 finally
92 {
93 _iterating.set(false);
94 }
95 }
96
97
98 @Override
99 public void succeeded()
100 {
101 if (!_iterating.compareAndSet(true,false))
102 iterate();
103 }
104
105 @Override
106 public void failed(Throwable x)
107 {
108 _callback.failed(x);
109 }
110
111 }