1 //
2 // ========================================================================
3 // Copyright (c) 1995-2015 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.AtomicReference;
22
23 /**
24 * <p>
25 * A callback to be used by driver code that needs to know whether the callback has been
26 * succeeded or failed (that is, completed) just after the asynchronous operation or not,
27 * typically because further processing depends on the callback being completed.
28 * The driver code competes with the asynchronous operation to complete the callback.
29 * </p>
30 * <p>
31 * If the callback is already completed, the driver code continues the processing,
32 * otherwise it suspends it. If it is suspended, the callback will be completed some time
33 * later, and {@link #resume()} or {@link #abort(Throwable)} will be called to allow the
34 * application to resume the processing.
35 * </p>
36 * Typical usage:
37 * <pre>
38 * CompletableCallback callback = new CompletableCallback()
39 * {
40 * @Override
41 * public void resume()
42 * {
43 * // continue processing
44 * }
45 *
46 * @Override
47 * public void abort(Throwable failure)
48 * {
49 * // abort processing
50 * }
51 * }
52 * asyncOperation(callback);
53 * boolean completed = callback.tryComplete();
54 * if (completed)
55 * // suspend processing, async operation not done yet
56 * else
57 * // continue processing, async operation already done
58 * </pre>
59 */
60 public abstract class CompletableCallback implements Callback
61 {
62 private final AtomicReference<State> state = new AtomicReference<>(State.IDLE);
63
64 @Override
65 public void succeeded()
66 {
67 while (true)
68 {
69 State current = state.get();
70 switch (current)
71 {
72 case IDLE:
73 {
74 if (state.compareAndSet(current, State.SUCCEEDED))
75 return;
76 break;
77 }
78 case COMPLETED:
79 {
80 if (state.compareAndSet(current, State.SUCCEEDED))
81 {
82 resume();
83 return;
84 }
85 break;
86 }
87 default:
88 {
89 throw new IllegalStateException(current.toString());
90 }
91 }
92 }
93 }
94
95 @Override
96 public void failed(Throwable x)
97 {
98 while (true)
99 {
100 State current = state.get();
101 switch (current)
102 {
103 case IDLE:
104 case COMPLETED:
105 {
106 if (state.compareAndSet(current, State.FAILED))
107 {
108 abort(x);
109 return;
110 }
111 break;
112 }
113 default:
114 {
115 throw new IllegalStateException(current.toString());
116 }
117 }
118 }
119 }
120
121 /**
122 * Callback method invoked when this callback is succeeded
123 * <em>after</em> a first call to {@link #tryComplete()}.
124 */
125 public abstract void resume();
126
127 /**
128 * Callback method invoked when this callback is failed.
129 * @param failure the throwable reprsenting the callback failure
130 */
131 public abstract void abort(Throwable failure);
132
133 /**
134 * Tries to complete this callback; driver code should call
135 * this method once <em>after</em> the asynchronous operation
136 * to detect whether the asynchronous operation has already
137 * completed or not.
138 *
139 * @return whether the attempt to complete was successful.
140 */
141 public boolean tryComplete()
142 {
143 while (true)
144 {
145 State current = state.get();
146 switch (current)
147 {
148 case IDLE:
149 {
150 if (state.compareAndSet(current, State.COMPLETED))
151 return true;
152 break;
153 }
154 case SUCCEEDED:
155 case FAILED:
156 {
157 return false;
158 }
159 default:
160 {
161 throw new IllegalStateException(current.toString());
162 }
163 }
164 }
165 }
166
167 private enum State
168 {
169 IDLE, SUCCEEDED, FAILED, COMPLETED
170 }
171 }