A support class for ListenableFuture implementations to manage their listeners. An instance contains a list of listeners, each with an associated Executor, and guarantees that every Runnable that is plain #add added will be executed after #execute() is called.
| 41 | |
| 42 | |
| 43 | @GwtIncompatible |
| 44 | public final class ExecutionList { |
| 45 | /** Logger to log exceptions caught when running runnables. */ |
| 46 | private static final Logger log = Logger.getLogger(ExecutionList.class.getName()); |
| 47 | |
| 48 | /** |
| 49 | * The runnable, executor pairs to execute. This acts as a stack threaded through the {@link |
| 50 | * RunnableExecutorPair#next} field. |
| 51 | */ |
| 52 | |
| 53 | @GuardedBy("this") |
| 54 | private RunnableExecutorPair runnables; |
| 55 | |
| 56 | @GuardedBy("this") |
| 57 | private boolean executed; |
| 58 | |
| 59 | /** Creates a new, empty {@link ExecutionList}. */ |
| 60 | |
| 61 | |
| 62 | public ExecutionList() {} |
| 63 | |
| 64 | /** |
| 65 | * Adds the {@code Runnable} and accompanying {@code Executor} to the list of listeners to |
| 66 | * execute. If execution has already begun, the listener is executed immediately. |
| 67 | * |
| 68 | * <p>When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See |
| 69 | * the discussion in the {@link ListenableFuture#addListener ListenableFuture.addListener} |
| 70 | * documentation. |
| 71 | */ |
| 72 | |
| 73 | |
| 74 | public void add(Runnable runnable, Executor executor) { |
| 75 | // Fail fast on a null. We throw NPE here because the contract of Executor states that it throws |
| 76 | // NPE on null listener, so we propagate that contract up into the add method as well. |
| 77 | checkNotNull(runnable, "Runnable was null."); |
| 78 | checkNotNull(executor, "Executor was null."); |
| 79 | |
| 80 | // Lock while we check state. We must maintain the lock while adding the new pair so that |
| 81 | // another thread can't run the list out from under us. We only add to the list if we have not |
| 82 | // yet started execution. |
| 83 | synchronized (this) { |
| 84 | if (!executed) { |
| 85 | runnables = new RunnableExecutorPair(runnable, executor, runnables); |
| 86 | return; |
| 87 | } |
| 88 | } |
| 89 | // Execute the runnable immediately. Because of scheduling this may end up getting called before |
| 90 | // some of the previously added runnables, but we're OK with that. If we want to change the |
| 91 | // contract to guarantee ordering among runnables we'd have to modify the logic here to allow |
| 92 | // it. |
| 93 | executeListener(runnable, executor); |
| 94 | } |
| 95 | |
| 96 | /** |
| 97 | * Runs this execution list, executing all existing pairs in the order they were added. However, |
| 98 | * note that listeners added after this point may be executed before those previously added, and |
| 99 | * note that the execution order of all listeners is ultimately chosen by the implementations of |
| 100 | * the supplied executors. |