package com.koushikdutta.async.future;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import com.koushikdutta.async.AsyncServer.AsyncSemaphore;
public class SimpleFuture<T> extends SimpleCancellable implements DependentFuture<T> {
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return cancel();
}
@Override
public boolean cancel() {
if (!super.cancel())
return false;
// still need to release any pending waiters
synchronized (this) {
exception = new CancellationException();
releaseWaiterLocked();
}
return true;
}
AsyncSemaphore waiter;
@Override
public T get() throws InterruptedException, ExecutionException {
AsyncSemaphore waiter;
synchronized (this) {
if (isCancelled() || isDone())
return getResult();
waiter = ensureWaiterLocked();
}
waiter.acquire();
return getResult();
}
private T getResult() throws ExecutionException {
if (exception != null)
throw new ExecutionException(exception);
return result;
}
@Override
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
AsyncSemaphore waiter;
synchronized (this) {
if (isCancelled() || isDone())
return getResult();
waiter = ensureWaiterLocked();
}
if (!waiter.tryAcquire(timeout, unit))
throw new TimeoutException();
return getResult();
}
@Override
public boolean setComplete() {
return setComplete((T)null);
}
private FutureCallback<T> handleCompleteLocked() {
// don't execute the callback inside the sync block... possible hangup
// read the callback value, and then call it outside the block.
// can't simply call this.callback.onCompleted directly outside the block,
// because that may result in a race condition where the callback changes once leaving
// the block.
FutureCallback<T> callback = this.callback;
// null out members to allow garbage collection
this.callback = null;
return callback;
}
private void handleCallbackUnlocked(FutureCallback<T> callback) {
if (callback != null)
callback.onCompleted(exception, result);
}
void releaseWaiterLocked() {
if (waiter != null) {
waiter.release();
waiter = null;
}
}
AsyncSemaphore ensureWaiterLocked() {
if (waiter == null)
waiter = new AsyncSemaphore();
return waiter;
}
Exception exception;
public boolean setComplete(Exception e) {
FutureCallback<T> callback;
synchronized (this) {
if (!super.setComplete())
return false;
exception = e;
releaseWaiterLocked();
callback = handleCompleteLocked();
}
handleCallbackUnlocked(callback);
return true;
}
T result;
public boolean setComplete(T value) {
FutureCallback<T> callback;
synchronized (this) {
if (!super.setComplete())
return false;
result = value;
releaseWaiterLocked();
callback = handleCompleteLocked();
}
handleCallbackUnlocked(callback);
return true;
}
public boolean setComplete(Exception e, T value) {
if (e != null)
return setComplete(e);
return setComplete(value);
}
public FutureCallback<T> getCompletionCallback() {
return new FutureCallback<T>() {
@Override
public void onCompleted(Exception e, T result) {
setComplete(e, result);
}
};
}
FutureCallback<T> callback;
@Override
public SimpleFuture<T> setCallback(FutureCallback<T> callback) {
// callback can only be changed or read/used inside a sync block
synchronized (this) {
this.callback = callback;
if (isDone())
callback = handleCompleteLocked();
else
callback = null;
}
handleCallbackUnlocked(callback);
return this;
}
@Override
public <C extends TransformFuture<?, T>> C then(C callback) {
callback.setParent(this);
setCallback(callback);
return callback;
}
@Override
public SimpleFuture<T> setParent(Cancellable parent) {
super.setParent(parent);
return this;
}
/**
* Reset the future for reuse.
* @return
*/
public SimpleFuture<T> reset() {
super.reset();
result = null;
exception = null;
waiter = null;
callback = null;
return this;
}
}