package org.limewire.concurrent;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicReference;
import org.limewire.util.ExceptionUtils;
import org.limewire.util.Objects;
/**
* An {@link AsyncFuture} that implements the {@link Runnable} interface.
*
* @see AsyncValueFuture
* @see FutureTask
*/
public class AsyncFutureTask<V> extends AsyncValueFuture<V>
implements RunnableAsyncFuture<V> {
/**
* This {@link Callable} is used by the default constructor.
* It makes it very easy to extend {@link AsyncFutureTask}
* and override the {@link #doRun()} method. The {@link Callable}
* does nothing but throw an {@link UnsupportedOperationException}
* and remind the user to override the {@link #doRun()} method.
*/
private static Callable<Object> NOP = new Callable<Object>() {
public Object call() {
throw new UnsupportedOperationException("Override doRun()");
}
};
/**
* We use this {@link AtomicReference} to manage the {@link Thread}
* that is executing this {@link AsyncFutureTask}.
*
* <p>NOTE: It doesn't have to be an {@link AtomicReference}. We could
* use a plain handle but this is very elegant.
*/
private final AtomicReference<Interruptible> thread
= new AtomicReference<Interruptible>(Interruptible.INIT);
/**
* The {@link Callable} that will provide the result value
* or throw an {@link Exception}.
*/
private final Callable<V> callable;
/**
* Creates an {@link AsyncFutureTask}
*/
@SuppressWarnings("unchecked")
public AsyncFutureTask() {
this((Callable<V>)NOP);
}
/**
* Creates an {@link AsyncFutureTask} with the given
* {@link Runnable} and result value
*/
public AsyncFutureTask(Runnable task, V result) {
this(Executors.callable(task, result));
}
/**
* Creates an {@link AsyncFutureTask} with the given {@link Callable}
*/
public AsyncFutureTask(Callable<V> callable) {
this.callable = Objects.nonNull(callable, "callable");
}
@Override
public final void run() {
if (preRun()) {
try {
doRun();
} catch (Throwable t) {
uncaughtException(t);
} finally {
postRun();
}
}
}
/**
* Called before {@link #doRun()} to initialize the
* current {@link Thread}. Returns true upon success.
*/
private boolean preRun() {
return thread.compareAndSet(Interruptible.INIT, new CurrentThread());
}
/**
* Called after {@link #doRun()} to cleanup the current {@link Thread}.
*/
private void postRun() {
// We must use synchronized here and in cancel(boolean) to
// ensure that Threads are not being interrupted after
// completion of the Task.
//
// Example: Thread X enters the run() method and passes the
// preRun() method. We call cancel(true) and the Interruptible
// handle is being replaced but we haven't called the interrupt()
// method yet. Thread X passes through the finally-block and
// moves on to execute the next Runnable in its parent's
// Executor queue. We call interrupt() and boom!
//
// The synchronized blocks make sure we're never calling after
// completion of the task!
synchronized (thread) {
thread.set(Interruptible.DONE);
}
}
/**
* The actual implementation of the {@link AsyncFutureTask}'s
* run method.
*/
protected void doRun() throws Exception {
V value = callable.call();
setValue(value);
}
/**
* Called for all {@link Throwable}s that are caught in the
* {@link #run()} method. The default implementation passes it
* on to {@link #setException(Throwable)} and it is forwarded
* to {@link ExceptionUtils#reportOrReturn(Throwable)} if it's
* of type {@link RuntimeException} or {@link Error}.
*
* <p>You may override this method to change the default behavior.
*/
protected void uncaughtException(Throwable t) {
setException(t);
if (t instanceof RuntimeException
|| t instanceof Error) {
ExceptionUtils.reportOrReturn(t);
}
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
boolean success = super.cancel(mayInterruptIfRunning);
if (success && mayInterruptIfRunning) {
// See postRun() regards the synchronized block!
synchronized (thread) {
thread.getAndSet(Interruptible.DONE).interrupt();
}
}
return success;
}
/**
* An interface for interruptible objects such as {@link Thread}s
*/
private static interface Interruptible {
/**
* An initial value for {@link AtomicReference}s that does nothing but
* allows us to use {@link AtomicReference#compareAndSet(Object, Object)}.
*/
public static final Interruptible INIT = new Interruptible() {
@Override
public void interrupt() {
}
};
/**
* An final value for {@link AtomicReference}s that does nothing but
* allows us to use {@link AtomicReference#compareAndSet(Object, Object)}.
*/
public static final Interruptible DONE = new Interruptible() {
@Override
public void interrupt() {
}
};
/**
* Interrupt!
*/
public void interrupt();
}
/**
* An implementation of {@link Interruptible} that holds a reference
* to the {@link Thread} (as returned by {@link Thread#currentThread()})
* the created it.
*/
private static class CurrentThread implements Interruptible {
private final Thread currentThread = Thread.currentThread();
@Override
public void interrupt() {
currentThread.interrupt();
}
}
}