+ private static final class DelegatingAsyncNotifyingListenableFutureTask<V> extends AsyncNotifyingListenableFutureTask<V> {
+ /**
+ * The executor used to run listener callbacks.
+ */
+ private final Executor listenerExecutor;
+
+ private DelegatingAsyncNotifyingListenableFutureTask(final Callable<V> callable, @Nullable final Executor listenerExecutor) {
+ super(callable);
+ this.listenerExecutor = Preconditions.checkNotNull(listenerExecutor);
+ }
+
+ private DelegatingAsyncNotifyingListenableFutureTask(final Runnable runnable, @Nullable final V result,
+ @Nullable final Executor listenerExecutor) {
+ super(runnable, result);
+ this.listenerExecutor = Preconditions.checkNotNull(listenerExecutor);
+ }
+
+ @Override
+ public void addListener(final Runnable listener, final Executor executor) {
+ // Wrap the listener Runnable in a DelegatingRunnable. If the specified executor is one that
+ // runs tasks in the same thread as the caller submitting the task
+ // (e.g. {@link com.google.common.util.concurrent.MoreExecutors#sameThreadExecutor}) and the
+ // listener is executed from the #done method, then the DelegatingRunnable will detect this
+ // via the ThreadLocal and submit the listener Runnable to the listenerExecutor.
+ //
+ // On the other hand, if this task is already complete, the call to ExecutionList#add in
+ // superclass will execute the listener Runnable immediately and, since the ThreadLocal won't be set,
+ // the DelegatingRunnable will run the listener Runnable inline.
+ super.addListener(new DelegatingRunnable(listener, listenerExecutor), executor);
+ }
+ }