All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.barchart.util.concurrent.FutureNotifierBase Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 2011-2012 Barchart, Inc. 
 *
 * All rights reserved. Licensed under the OSI BSD License.
 *
 * http://www.opensource.org/licenses/bsd-license.php
 */
package com.barchart.util.concurrent;

import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * An implementation of FutureCallback that does not do any actual computation,
 * but just serves as a communication proxy between the executor and listeners.
 * 
 * This is a base class suitable for subclassing to return the correct object
 * type in succeed() and fail(). Ad-hoc notifiers can use the FutureNotifier
 * subclass to avoid additional parameterization.
 * 
 * @param 
 *            The result type
 * @param 
 *            The subclass being defined (for return value parameterization)
 */
public class FutureNotifierBase> implements
		FutureCallback, FutureListener {

	private final static Logger log = LoggerFactory
			.getLogger(FutureNotifierBase.class);

	/** Callback listeners */
	private final List> listeners =
			new CopyOnWriteArrayList>();
	private final Lock callbackLock = new ReentrantLock();

	/** Synchronization control for FutureNotifier */
	private final Sync sync;

	/**
	 * Creates a FutureNotifier that notifies any callbacks upon
	 * completion.
	 */
	public FutureNotifierBase() {
		sync = new Sync();
	}

	/**
	 * Creates a FutureNotifier that notifies any callbacks upon
	 * completion. If a runner thread is specified here, cancel() will attempt
	 * to interrupt it if requested.
	 */
	public FutureNotifierBase(final Thread runner) {
		sync = new Sync(runner);
	}

	@Override
	public boolean isCancelled() {
		return sync.innerIsCancelled();
	}

	@Override
	public boolean isDone() {
		return sync.innerIsDone();
	}

	@Override
	public boolean cancel(final boolean mayInterruptIfRunning) {
		return sync.innerCancel(mayInterruptIfRunning);
	}

	/**
	 * @throws CancellationException
	 *             {@inheritDoc}
	 */
	@Override
	public V get() throws InterruptedException, ExecutionException {
		return sync.innerGet();
	}

	@Override
	public V getUnchecked() {
		try {
			return sync.innerGet();
		} catch (final Exception e) {
			return null;
		}
	}

	/**
	 * @throws CancellationException
	 *             {@inheritDoc}
	 */
	@Override
	public V get(final long timeout, final TimeUnit unit)
			throws InterruptedException, ExecutionException, TimeoutException {
		return sync.innerGet(unit.toNanos(timeout));
	}

	/**
	 * Result listener for chaining callbacks together.
	 */
	@Override
	public void resultAvailable(final Future result) {
		try {
			succeed(result.get());
		} catch (final ExecutionException e) {
			fail(e.getCause());
		} catch (final Exception e) {
			fail(e);
		}
	}

	/**
	 * Protected method invoked when this task transitions to state
	 * isDone (whether normally or via cancellation). The default
	 * implementation does nothing. Subclasses may override this method to
	 * invoke completion callbacks or perform bookkeeping. Note that you can
	 * query status inside the implementation of this method to determine
	 * whether this task has been cancelled.
	 */
	protected void done() {
		callbackLock.lock();
		try {
			for (final FutureListener l : listeners) {
				try {
					l.resultAvailable(this);
				} catch (final Exception ex) {
					log.warn("Unhandled exception in callback", ex);
				}
			}
		} finally {
			callbackLock.unlock();
		}
	}

	/**
	 * Sets the result of this Future to the given value unless this future has
	 * already been set or has been cancelled. This method is invoked internally
	 * by the run method upon successful completion of the computation.
	 * 
	 * @param v
	 *            the value
	 */
	protected void set(final V v) {
		if (isDone()) {
			throw new IllegalStateException("Future already completed");
		}
		sync.innerSet(v);
	}

	/**
	 * Causes this future to report an ExecutionException with the
	 * given throwable as its cause, unless this Future has already been set or
	 * has been cancelled. This method is invoked internally by the run
	 * method upon failure of the computation.
	 * 
	 * @param t
	 *            the cause of failure
	 */
	protected void setException(final Throwable t) {
		if (isDone()) {
			throw new IllegalStateException("Future already completed");
		}
		sync.innerSetException(t);
	}

	@SuppressWarnings("unchecked")
	@Override
	public T addResultListener(final FutureListener listener) {
		callbackLock.lock();
		try {
			listeners.add(listener);
			if (isDone()) {
				try {
					listener.resultAvailable(this);
				} catch (final Exception ex) {
					log.warn("Unhandled exception in callback", ex);
				}
			} else if (listener instanceof CancellableFutureNotifier) {
				// MAGIC - register as parent for cancel calls
				((CancellableFutureNotifier) listener)
						.setCancelCallback(this);
			}
		} finally {
			callbackLock.unlock();
		}
		return (T) this;
	}

	@SuppressWarnings("unchecked")
	@Override
	public T fail(final Throwable error) {
		setException(error);
		return (T) this;
	}

	@SuppressWarnings("unchecked")
	@Override
	public T succeed(final V result) {
		set(result);
		return (T) this;
	}

	/**
	 * Synchronization control for FutureNotifier. Note that this must be a
	 * non-static inner class in order to invoke the protected done
	 * method. For clarity, all inner class support methods are same as outer,
	 * prefixed with "inner".
	 * 
	 * Uses AQS sync state to represent run status
	 */
	private final class Sync extends AbstractQueuedSynchronizer {
		private static final long serialVersionUID = -7828117401763700385L;

		/** State value representing that task ran */
		private static final int RAN = 1;
		/** State value representing that task was cancelled */
		private static final int CANCELLED = 2;

		/** The result to return from get() */
		private V result = null;
		/** The exception to throw from get() */
		private Throwable exception = null;

		/**
		 * The thread running task. When nulled after set/cancel, this indicates
		 * that the results are accessible. Must be volatile, to ensure
		 * visibility upon completion.
		 */
		private volatile Thread runner = null;

		Sync() {
		}

		Sync(final Thread runner_) {
			runner = runner_;
		}

		private boolean ranOrCancelled(final int state) {
			return (state & (RAN | CANCELLED)) != 0;
		}

		/**
		 * Implements AQS base acquire to succeed if ran or cancelled
		 */
		@Override
		protected int tryAcquireShared(final int ignore) {
			return innerIsDone() ? 1 : -1;
		}

		/**
		 * Implements AQS base release to always signal after setting final done
		 * status by nulling runner thread.
		 */
		@Override
		protected boolean tryReleaseShared(final int ignore) {
			runner = null;
			return true;
		}

		boolean innerIsCancelled() {
			return getState() == CANCELLED;
		}

		boolean innerIsDone() {
			return ranOrCancelled(getState()) && runner == null;
		}

		V innerGet() throws InterruptedException, ExecutionException {
			acquireSharedInterruptibly(0);
			if (getState() == CANCELLED) {
				throw new CancellationException();
			}
			if (exception != null) {
				throw new ExecutionException(exception);
			}
			return result;
		}

		V innerGet(final long nanosTimeout) throws InterruptedException,
				ExecutionException, TimeoutException {
			if (!tryAcquireSharedNanos(0, nanosTimeout)) {
				throw new TimeoutException();
			}
			if (getState() == CANCELLED) {
				throw new CancellationException();
			}
			if (exception != null) {
				throw new ExecutionException(exception);
			}
			return result;
		}

		void innerSet(final V v) {
			for (;;) {
				final int s = getState();
				if (s == RAN) {
					return;
				}
				if (s == CANCELLED) {
					// aggressively release to set runner to null,
					// in case we are racing with a cancel request
					// that will try to interrupt runner
					releaseShared(0);
					return;
				}
				if (compareAndSetState(s, RAN)) {
					result = v;
					releaseShared(0);
					done();
					return;
				}
			}
		}

		void innerSetException(final Throwable t) {
			for (;;) {
				final int s = getState();
				if (s == RAN) {
					return;
				}
				if (s == CANCELLED) {
					// aggressively release to set runner to null,
					// in case we are racing with a cancel request
					// that will try to interrupt runner
					releaseShared(0);
					return;
				}
				if (compareAndSetState(s, RAN)) {
					exception = t;
					result = null;
					releaseShared(0);
					done();
					return;
				}
			}
		}

		boolean innerCancel(final boolean mayInterruptIfRunning) {
			for (;;) {
				final int s = getState();
				if (ranOrCancelled(s)) {
					return false;
				}
				if (compareAndSetState(s, CANCELLED)) {
					break;
				}
			}
			if (mayInterruptIfRunning) {
				final Thread r = runner;
				if (r != null) {
					r.interrupt();
				}
			}
			releaseShared(0);
			done();
			return true;
		}

	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy