/** * Constructs a runnable future with a callable work unit. * * @param recurring boolean to indicate if this task can run multiple times, and thus must be reset after each run * @param task callable to be run * @param executingExecutor Executor task will be run on for possible listener optimization, or {@code null} */ public ListenableFutureTask(boolean recurring, Callable<T> task, Executor executingExecutor) { super(task); this.listenerHelper = new RunnableListenerHelper(true); this.recurring = recurring; this.callable = task; this.executingExecutor = executingExecutor; }
@Override public void addListener(Runnable listener, Executor executor, ListenerOptimizationStrategy optimize) { listenerHelper.addListener(listener, executor == executingExecutor && (optimize == ListenerOptimizationStrategy.SingleThreadIfExecutorMatchOrDone | optimize == ListenerOptimizationStrategy.SingleThreadIfExecutorMatch) ? null : executor, optimize == ListenerOptimizationStrategy.SingleThreadIfExecutorMatchOrDone ? null : executor); }
/** * Can not be overridden, please use {@link #addListener(Runnable)} as an alternative. */ @Override protected final void done() { executingExecutor = null; callable = null; listenerHelper.callListeners(); }
@Test (expected = RuntimeException.class) public void runListenerThrowExecptionTest() { onceHelper.callListeners(); TestRunnable tr = new TestRunnable() { @Override public void handleRunFinish() { throw new SuppressedStackRuntimeException(); } }; onceHelper.addListener(tr); fail("Execption should have thrown"); }
@Test public void removeListenerAfterCallTest() { TestRunnable onceTR = new TestRunnable(); TestRunnable repeatedTR = new TestRunnable(); assertFalse(onceHelper.removeListener(onceTR)); assertFalse(repeatedHelper.removeListener(repeatedTR)); onceHelper.addListener(onceTR); repeatedHelper.addListener(repeatedTR); onceHelper.callListeners(); repeatedHelper.callListeners(); assertFalse(onceHelper.removeListener(onceTR)); assertTrue(repeatedHelper.removeListener(repeatedTR)); }
@Test public void registeredListenerCountTest() { assertEquals(0, onceHelper.registeredListenerCount()); assertEquals(0, repeatedHelper.registeredListenerCount()); onceHelper.addListener(DoNothingRunnable.instance()); repeatedHelper.addListener(DoNothingRunnable.instance()); assertEquals(1, onceHelper.registeredListenerCount()); assertEquals(1, repeatedHelper.registeredListenerCount()); onceHelper.callListeners(); repeatedHelper.callListeners(); assertEquals(0, onceHelper.registeredListenerCount()); assertEquals(1, repeatedHelper.registeredListenerCount()); repeatedHelper.addListener(DoNothingRunnable.instance(), SameThreadSubmitterExecutor.instance()); assertEquals(2, repeatedHelper.registeredListenerCount()); }
@Test public void clearListenersTest() { TestRunnable onceTR = new TestRunnable(); TestRunnable repeatedTR = new TestRunnable(); onceHelper.addListener(onceTR); repeatedHelper.addListener(repeatedTR); onceHelper.clearListeners(); repeatedHelper.clearListeners(); onceHelper.callListeners(); repeatedHelper.callListeners(); assertFalse(onceTR.ranOnce()); assertFalse(repeatedTR.ranOnce()); }
@Test public void getSubscribedListenersMixedExecutionTest() { TestRunnable tr1 = new TestRunnable(); TestRunnable tr2 = new TestRunnable(); onceHelper.addListener(tr1); onceHelper.addListener(tr2, SameThreadSubmitterExecutor.instance()); assertTrue(onceHelper.getSubscribedListeners().contains(tr1)); assertTrue(onceHelper.getSubscribedListeners().contains(tr2)); }
@Test public void getSubscribedListenersTest() { assertTrue(onceHelper.getSubscribedListeners().isEmpty()); TestRunnable tr = new TestRunnable(); onceHelper.addListener(tr); assertTrue(onceHelper.getSubscribedListeners().contains(tr)); onceHelper.removeListener(tr); assertTrue(onceHelper.getSubscribedListeners().isEmpty()); }
private void removeListenerTest(Executor executor) { TestRunnable onceTR = new TestRunnable(); TestRunnable repeatedTR = new TestRunnable(); assertFalse(onceHelper.removeListener(onceTR)); assertFalse(repeatedHelper.removeListener(repeatedTR)); onceHelper.addListener(onceTR, executor); repeatedHelper.addListener(repeatedTR, executor); // should be false for the opposite assertFalse(onceHelper.removeListener(repeatedTR)); assertFalse(repeatedHelper.removeListener(onceTR)); assertTrue(onceHelper.removeListener(onceTR)); assertTrue(repeatedHelper.removeListener(repeatedTR)); }
/** * Will call all listeners that are registered with this helper. If any listeners were provided * without an executor, they will execute in the calling thread. No exceptions will be thrown * in this calling thread if any exceptions occur from the listeners. * <p> * If calling multiple times, this will only have an effect if constructed with a {@code false}, * indicating that listeners can expect to be called multiple times. In which case all * listeners that have registered will be called again. If this was constructed with the * expectation of only calling once an {@link IllegalStateException} will be thrown on * subsequent calls. */ public void callListeners() { synchronized (listenersLock) { if (callOnce) { if (done) { throw new IllegalStateException("Already called listeners"); } else { done = true; } } doCallListeners(); } }
if (addingFromCallingThread && executorListeners != null) { executorListeners = copyAndAdd(executorListeners, new Pair<>(listener, queueExecutor)); } else { if (executorListeners == null) { if (addingFromCallingThread && inThreadListeners != null) { inThreadListeners = copyAndAdd(inThreadListeners, listener); } else { if (inThreadListeners == null) {
@Override public void run() { repeatedHelper.removeListener(removedRunnable); } }, executor);
assertEquals(0, future.listenerHelper.registeredListenerCount()); // empty to start assertEquals(1, future.listenerHelper.registeredListenerCount()); // should now have once now that the runnable has not run yet assertEquals(0, future.listenerHelper.registeredListenerCount()); // empty after listener calls assertEquals(0, future.listenerHelper.registeredListenerCount()); // still empty after future ran
private void removeListenerFromCallingThreadTest(Executor executor) { final TestRunnable removedRunnable = new TestRunnable(); repeatedHelper.addListener(new TestRunnable()); repeatedHelper.addListener(new TestRunnable(), executor); repeatedHelper.addListener(new TestRunnable(), executor); repeatedHelper.addListener(new Runnable() { @Override public void run() { repeatedHelper.removeListener(removedRunnable); } }, executor); repeatedHelper.addListener(new TestRunnable(), executor); repeatedHelper.addListener(new TestRunnable(), executor); repeatedHelper.addListener(removedRunnable, executor); repeatedHelper.addListener(new TestRunnable(), executor); repeatedHelper.addListener(new TestRunnable(), executor); repeatedHelper.addListener(new TestRunnable()); repeatedHelper.callListeners(); // call again and verify it did not run again repeatedHelper.callListeners(); assertEquals(1, removedRunnable.getRunCount()); }
@Test public void getSubscribedListenersInThreadOnlyTest() { TestRunnable tr = new TestRunnable(); onceHelper.addListener(tr); assertTrue(onceHelper.getSubscribedListeners().contains(tr)); }
/** * Will call all listeners that are registered with this helper. If any listeners were provided * without an executor, they will execute in the calling thread. No exceptions will be thrown * in this calling thread if any exceptions occur from the listeners. * <p> * If calling multiple times, this will only have an effect if constructed with a {@code false}, * indicating that listeners can expect to be called multiple times. In which case all * listeners that have registered will be called again. If this was constructed with the * expectation of only calling once an {@link IllegalStateException} will be thrown on * subsequent calls. */ public void callListeners() { synchronized (listenersLock) { if (callOnce) { if (done) { throw new IllegalStateException("Already called listeners"); } else { done = true; } } doCallListeners(); } }
if (addingFromCallingThread && executorListeners != null) { executorListeners = copyAndAdd(executorListeners, new Pair<>(listener, queueExecutor)); } else { if (executorListeners == null) { if (addingFromCallingThread && inThreadListeners != null) { inThreadListeners = copyAndAdd(inThreadListeners, listener); } else { if (inThreadListeners == null) {
@Test public void runListenerCatchExecptionTest() { TestRunnable tr = new TestRunnable() { @Override public void handleRunFinish() { throw new SuppressedStackRuntimeException(); } }; onceHelper.addListener(tr); onceHelper.callListeners(); assertTrue(tr.ranOnce()); }
@Override public void addListener(Runnable listener, Executor executor, ListenerOptimizationStrategy optimize) { listenerHelper.addListener(listener, executor == executingExecutor && (optimize == ListenerOptimizationStrategy.SingleThreadIfExecutorMatchOrDone | optimize == ListenerOptimizationStrategy.SingleThreadIfExecutorMatch) ? null : executor, optimize == ListenerOptimizationStrategy.SingleThreadIfExecutorMatchOrDone ? null : executor); }