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 * @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 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 }