package com.firefly.utils.concurrent; import java.util.concurrent.atomic.AtomicReference; /** * <p> * A callback to be used by driver code that needs to know whether the callback * has been succeeded or failed (that is, completed) just after the asynchronous * operation or not, typically because further processing depends on the * callback being completed. The driver code competes with the asynchronous * operation to complete the callback. * </p> * <p> * If the callback is already completed, the driver code continues the * processing, otherwise it suspends it. If it is suspended, the callback will * be completed some time later, and {@link #resume()} or * {@link #abort(Throwable)} will be called to allow the application to resume * the processing. * </p> * Typical usage: * * <pre> * CompletableCallback callback = new CompletableCallback() * { * @Override * public void resume() * { * // continue processing * } * * @Override * public void abort(Throwable failure) * { * // abort processing * } * } * asyncOperation(callback); * boolean completed = callback.tryComplete(); * if (completed) * // suspend processing, async operation not done yet * else * // continue processing, async operation already done * </pre> */ public abstract class CompletableCallback implements Callback { private final AtomicReference<State> state = new AtomicReference<>(State.IDLE); @Override public void succeeded() { while (true) { State current = state.get(); switch (current) { case IDLE: { if (state.compareAndSet(current, State.SUCCEEDED)) return; break; } case COMPLETED: { if (state.compareAndSet(current, State.SUCCEEDED)) { resume(); return; } break; } case FAILED: { return; } default: { throw new IllegalStateException(current.toString()); } } } } @Override public void failed(Throwable x) { while (true) { State current = state.get(); switch (current) { case IDLE: case COMPLETED: { if (state.compareAndSet(current, State.FAILED)) { abort(x); return; } break; } case FAILED: { return; } default: { throw new IllegalStateException(current.toString()); } } } } /** * Callback method invoked when this callback is succeeded <em>after</em> a * first call to {@link #tryComplete()}. */ public abstract void resume(); /** * Callback method invoked when this callback is failed. * * @param failure * the throwable reprsenting the callback failure */ public abstract void abort(Throwable failure); /** * Tries to complete this callback; driver code should call this method once * <em>after</em> the asynchronous operation to detect whether the * asynchronous operation has already completed or not. * * @return whether the attempt to complete was successful. */ public boolean tryComplete() { while (true) { State current = state.get(); switch (current) { case IDLE: { if (state.compareAndSet(current, State.COMPLETED)) return true; break; } case SUCCEEDED: case FAILED: { return false; } default: { throw new IllegalStateException(current.toString()); } } } } private enum State { IDLE, SUCCEEDED, FAILED, COMPLETED } }