package jalse.actions; import static jalse.actions.Actions.emptyActionBindings; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.locks.StampedLock; /** * An abstract implementation of {@link SchedulableActionContext} that is designed to be used with * {@link ExecutorService}. This is a convenience class for creating an {@link ActionEngine}. * * @author Elliot Ford * * @param <T> * Actor type. */ public abstract class AbstractFutureActionContext<T> extends BaseActionContext<T> { private final StampedLock lock; private Future<?> future; /** * Creates a new instance of AbstractFutureActionContext with the supplied engine and action. * * @param engine * Parent engine. * @param action * Action this context is for. */ protected AbstractFutureActionContext(final ActionEngine engine, final Action<T> action) { this(engine, action, emptyActionBindings()); } /** * Creates a new instance of AbstractFutureActionContext with the supplied engine, action and * source bindings. * * @param engine * Parent engine. * @param action * The action this context is for. * @param sourceBindings * Bindings to shallow copy. */ protected AbstractFutureActionContext(final ActionEngine engine, final Action<T> action, final ActionBindings sourceBindings) { super(engine, action, sourceBindings); lock = new StampedLock(); future = null; } @Override public void await() throws InterruptedException { if (isPeriodic()) { throw new UnsupportedOperationException("Cannot await periodic actions"); } final Future<?> f = getFuture(); if (f == null || f.isDone()) { // No need to wait return; } try { f.get(); // Await execution completion or cancel } catch (final ExecutionException e) {} } @Override public boolean cancel() { final Future<?> f = getFuture(); return f != null && f.cancel(true); } /** * Gets the future associated to the action task. * * @return Future or null if none has been set. */ protected Future<?> getFuture() { long stamp = lock.tryOptimisticRead(); Future<?> future = this.future; if (!lock.validate(stamp)) { // Not valid so wait for read lock stamp = lock.readLock(); try { future = this.future; } finally { lock.unlockRead(stamp); } } return future; } @Override public boolean isCancelled() { final Future<?> f = getFuture(); return f != null && f.isCancelled(); } @Override public boolean isDone() { final Future<?> f = getFuture(); return f != null && f.isDone(); } /** * Sets the future of the associated action task. * * @param future * Future to set. */ protected void setFuture(final Future<?> future) { final long stamp = lock.writeLock(); try { this.future = future; } finally { lock.unlockWrite(stamp); } } }