com.firefly.utils.concurrent.CompletableCallback Maven / Gradle / Ivy
package com.firefly.utils.concurrent;
import java.util.concurrent.atomic.AtomicReference;
/**
*
* 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.
*
*
* 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.
*
* Typical usage:
*
*
* 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
*
*/
public abstract class CompletableCallback implements Callback {
private final AtomicReference 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 after 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
* after 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
}
}