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