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   * 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   *     &#64;Override
39   *     public void resume()
40   *     {
41   *         // continue processing
42   *     }
43   *
44   *     &#64;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 }