View Javadoc

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   * <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   *     &#64;Override
41   *     public void resume()
42   *     {
43   *         // continue processing
44   *     }
45   *
46   *     &#64;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                  case FAILED:
88                  {
89                      return;
90                  }
91                  default:
92                  {
93                      throw new IllegalStateException(current.toString());
94                  }
95              }
96          }
97      }
98  
99      @Override
100     public void failed(Throwable x)
101     {
102         while (true)
103         {
104             State current = state.get();
105             switch (current)
106             {
107                 case IDLE:
108                 case COMPLETED:
109                 {
110                     if (state.compareAndSet(current, State.FAILED))
111                     {
112                         abort(x);
113                         return;
114                     }
115                     break;
116                 }
117                 case FAILED:
118                 {
119                     return;
120                 }
121                 default:
122                 {
123                     throw new IllegalStateException(current.toString());
124                 }
125             }
126         }
127     }
128 
129     /**
130      * Callback method invoked when this callback is succeeded
131      * <em>after</em> a first call to {@link #tryComplete()}.
132      */
133     public abstract void resume();
134 
135     /**
136      * Callback method invoked when this callback is failed.
137      * @param failure the throwable reprsenting the callback failure
138      */
139     public abstract void abort(Throwable failure);
140 
141     /**
142      * Tries to complete this callback; driver code should call
143      * this method once <em>after</em> the asynchronous operation
144      * to detect whether the asynchronous operation has already
145      * completed or not.
146      *
147      * @return whether the attempt to complete was successful.
148      */
149     public boolean tryComplete()
150     {
151         while (true)
152         {
153             State current = state.get();
154             switch (current)
155             {
156                 case IDLE:
157                 {
158                     if (state.compareAndSet(current, State.COMPLETED))
159                         return true;
160                     break;
161                 }
162                 case SUCCEEDED:
163                 case FAILED:
164                 {
165                     return false;
166                 }
167                 default:
168                 {
169                     throw new IllegalStateException(current.toString());
170                 }
171             }
172         }
173     }
174 
175     private enum State
176     {
177         IDLE, SUCCEEDED, FAILED, COMPLETED
178     }
179 }