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