View Javadoc

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   *     &#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                  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 }