package edu.washington.escience.myria.util.concurrent; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import com.google.common.base.Preconditions; import edu.washington.escience.myria.DbException; import edu.washington.escience.myria.util.JVMUtils; /** * The {@link ExecutionFuture} implementation that is Callable and Runnable. The state of the future is set * automatically by the execution of this task. * * @param <T> The return type. */ public class ExecutableExecutionFuture<T> extends OperationFutureBase<T> implements ExecutionFuture<T>, Callable<T>, Runnable { /** * The {@link Callable} who caused the creation of this future. * */ private final Callable<T> callable; /** * The thread who is executing the task. * */ private volatile Thread executingThread; /** * The lock to make sure the thread interrupt action is correct in {@link this#cancel(boolean)}. * */ private final Object interruptedCheckingLock = new Object(); /** * Creates a new instance. * * @param callable the {@link Callable} who caused the creation of this future. * * @param cancellable {@code true} if and only if this future can be canceled */ public ExecutableExecutionFuture(final Callable<T> callable, final boolean cancellable) { super(cancellable); Preconditions.checkNotNull(callable); this.callable = callable; } /** * Creates a new instance. * * @param runnable the {@link Runnable} who caused the creation of this future. * * @param cancellable {@code true} if and only if this future can be canceled */ public ExecutableExecutionFuture(final Runnable runnable, final boolean cancellable) { this(Executors.callable(runnable, (T) null), cancellable); } @Override public final ExecutionFuture<T> sync() throws InterruptedException, DbException { sync0(); return this; } @Override public final ExecutionFuture<T> syncUninterruptibly() throws DbException { super.syncUninterruptibly0(); return this; } @Override public final ExecutionFuture<T> await() throws InterruptedException { super.await0(); return this; } @Override public final ExecutionFuture<T> awaitUninterruptibly() { super.awaitUninterruptibly0(); return this; } /** * running state. * */ protected static final int RUNNING = 2; @Override public boolean cancel(final boolean mayInterruptIfRunning) { if (!isCancellable()) { return false; } if (isDone()) { return isCancelled(); } if (!super.compareAndSetState(READY, CANCELL)) { // already running or already done if (mayInterruptIfRunning) { Thread t = executingThread; if (t != null) { // cancel in running if (t == Thread.currentThread()) { cancel(); } else if (super.compareAndSetState(RUNNING, CANCELL)) { synchronized (interruptedCheckingLock) { t = executingThread; if (t != null) { t.interrupt(); } } this.awaitUninterruptibly(); wakeupWaitersAndNotifyListeners(); } } } } else { wakeupWaitersAndNotifyListeners(); } return isCancelled(); } @Override protected boolean doCancel() { return this.cancel(true); } @Override public T get() throws InterruptedException, ExecutionException { this.await(); this.checkFailure(); return getResult(); } /** * @throws ExecutionException if there's any error occurred during the execution of the {@link Runnable} or * {@link Callable} * */ private void checkFailure() throws ExecutionException { if (isDone() && !isSuccess()) { throw new ExecutionException(getCause()); } } @Override public T get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { this.await(timeout, unit); if (isDone()) { this.checkFailure(); return getResult(); } return null; } @Override public Callable<T> getCallable() { return callable; } @Override public ExecutionFuture<T> addListener(final OperationFutureListener listener) { super.addListener0(listener); return this; } @Override public ExecutionFuture<T> removeListener(final OperationFutureListener listener) { super.removeListener0(listener); return this; } @Override public T call() throws Exception { if (!compareAndSetState(READY, RUNNING)) { if (isDone()) { return getResult(); } else { throw new RejectedExecutionException( "Another thread (" + this.executingThread + ") is executing this task"); } } this.executingThread = Thread.currentThread(); try { T result = null; try { if (Thread.interrupted()) { throw new InterruptedException(); } result = this.callable.call(); if (Thread.interrupted()) { throw new InterruptedException(); } } catch (InterruptedException e) { if (isCancelled()) { // interrupted because the operation is cancelled. return null; } else { // unexpected interrupted, for example caused by shutdownNow throw e; } } if (setSuccess0(result)) { return result; } else { return null; } } catch (Throwable e) { setFailure0(e); if (e instanceof OutOfMemoryError) { JVMUtils.shutdownVM(e); } if (e instanceof Exception) { throw e; } return getResult(); } finally { synchronized (interruptedCheckingLock) { this.executingThread = null; if (Thread.currentThread().isInterrupted()) { if (!isCancelled()) { throw new InterruptedException(); } } } } } @Override public void run() { try { this.call(); } catch (Exception e) { throw new Error(e); } } @Override public ExecutableExecutionFuture<T> addPreListener(final OperationFutureListener listener) { super.addPreListener0(listener); return this; } }