|  | /* | 
|  | * Written by Doug Lea with assistance from members of JCP JSR-166 | 
|  | * Expert Group and released to the public domain, as explained at | 
|  | * http://creativecommons.org/publicdomain/zero/1.0/ | 
|  | * Other contributors include Andrew Wright, Jeffrey Hayes, | 
|  | * Pat Fisher, Mike Judd. | 
|  | */ | 
|  |  | 
|  | package jsr166; | 
|  |  | 
|  | import static java.util.concurrent.TimeUnit.MILLISECONDS; | 
|  | import static java.util.concurrent.TimeUnit.NANOSECONDS; | 
|  | import static java.util.concurrent.TimeUnit.SECONDS; | 
|  |  | 
|  | import java.util.ArrayList; | 
|  | import java.util.List; | 
|  | import java.util.NoSuchElementException; | 
|  | import java.util.concurrent.Callable; | 
|  | import java.util.concurrent.CancellationException; | 
|  | import java.util.concurrent.CountDownLatch; | 
|  | import java.util.concurrent.ExecutionException; | 
|  | import java.util.concurrent.Executors; | 
|  | import java.util.concurrent.ExecutorService; | 
|  | import java.util.concurrent.Future; | 
|  | import java.util.concurrent.FutureTask; | 
|  | import java.util.concurrent.TimeoutException; | 
|  | import java.util.concurrent.atomic.AtomicInteger; | 
|  |  | 
|  | import junit.framework.Test; | 
|  | import junit.framework.TestSuite; | 
|  |  | 
|  | public class FutureTaskTest extends JSR166TestCase { | 
|  |  | 
|  | // android-note: Removed because the CTS runner does a bad job of | 
|  | // retrying tests that have suite() declarations. | 
|  | // | 
|  | // public static void main(String[] args) { | 
|  | //     main(suite(), args); | 
|  | // } | 
|  | // public static Test suite() { | 
|  | //     return new TestSuite(FutureTaskTest.class); | 
|  | // } | 
|  |  | 
|  | void checkIsDone(Future<?> f) { | 
|  | assertTrue(f.isDone()); | 
|  | assertFalse(f.cancel(false)); | 
|  | assertFalse(f.cancel(true)); | 
|  | if (f instanceof PublicFutureTask) { | 
|  | PublicFutureTask pf = (PublicFutureTask) f; | 
|  | assertEquals(1, pf.doneCount()); | 
|  | assertFalse(pf.runAndReset()); | 
|  | assertEquals(1, pf.doneCount()); | 
|  | Object r = null; Object exInfo = null; | 
|  | try { | 
|  | r = f.get(); | 
|  | } catch (CancellationException t) { | 
|  | exInfo = CancellationException.class; | 
|  | } catch (ExecutionException t) { | 
|  | exInfo = t.getCause(); | 
|  | } catch (Throwable t) { | 
|  | threadUnexpectedException(t); | 
|  | } | 
|  |  | 
|  | // Check that run and runAndReset have no effect. | 
|  | int savedRunCount = pf.runCount(); | 
|  | pf.run(); | 
|  | pf.runAndReset(); | 
|  | assertEquals(savedRunCount, pf.runCount()); | 
|  | try { | 
|  | assertSame(r, f.get()); | 
|  | } catch (CancellationException t) { | 
|  | assertSame(exInfo, CancellationException.class); | 
|  | } catch (ExecutionException t) { | 
|  | assertSame(exInfo, t.getCause()); | 
|  | } catch (Throwable t) { | 
|  | threadUnexpectedException(t); | 
|  | } | 
|  | assertTrue(f.isDone()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void checkNotDone(Future<?> f) { | 
|  | assertFalse(f.isDone()); | 
|  | assertFalse(f.isCancelled()); | 
|  | if (f instanceof PublicFutureTask) { | 
|  | PublicFutureTask pf = (PublicFutureTask) f; | 
|  | assertEquals(0, pf.doneCount()); | 
|  | assertEquals(0, pf.setCount()); | 
|  | assertEquals(0, pf.setExceptionCount()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void checkIsRunning(Future<?> f) { | 
|  | checkNotDone(f); | 
|  | if (f instanceof FutureTask) { | 
|  | FutureTask ft = (FutureTask<?>) f; | 
|  | // Check that run methods do nothing | 
|  | ft.run(); | 
|  | if (f instanceof PublicFutureTask) { | 
|  | PublicFutureTask pf = (PublicFutureTask) f; | 
|  | int savedRunCount = pf.runCount(); | 
|  | pf.run(); | 
|  | assertFalse(pf.runAndReset()); | 
|  | assertEquals(savedRunCount, pf.runCount()); | 
|  | } | 
|  | checkNotDone(f); | 
|  | } | 
|  | } | 
|  |  | 
|  | <T> void checkCompletedNormally(Future<T> f, T expected) { | 
|  | checkIsDone(f); | 
|  | assertFalse(f.isCancelled()); | 
|  |  | 
|  | try { | 
|  | assertSame(expected, f.get()); | 
|  | } catch (Throwable fail) { threadUnexpectedException(fail); } | 
|  | try { | 
|  | assertSame(expected, f.get(5L, SECONDS)); | 
|  | } catch (Throwable fail) { threadUnexpectedException(fail); } | 
|  | } | 
|  |  | 
|  | void checkCancelled(Future<?> f) { | 
|  | checkIsDone(f); | 
|  | assertTrue(f.isCancelled()); | 
|  |  | 
|  | try { | 
|  | f.get(); | 
|  | shouldThrow(); | 
|  | } catch (CancellationException success) { | 
|  | } catch (Throwable fail) { threadUnexpectedException(fail); } | 
|  |  | 
|  | try { | 
|  | f.get(5L, SECONDS); | 
|  | shouldThrow(); | 
|  | } catch (CancellationException success) { | 
|  | } catch (Throwable fail) { threadUnexpectedException(fail); } | 
|  | } | 
|  |  | 
|  | void tryToConfuseDoneTask(PublicFutureTask pf) { | 
|  | pf.set(new Object()); | 
|  | pf.setException(new Error()); | 
|  | for (boolean mayInterruptIfRunning : new boolean[] { true, false }) { | 
|  | pf.cancel(mayInterruptIfRunning); | 
|  | } | 
|  | } | 
|  |  | 
|  | void checkCompletedAbnormally(Future<?> f, Throwable t) { | 
|  | checkIsDone(f); | 
|  | assertFalse(f.isCancelled()); | 
|  |  | 
|  | try { | 
|  | f.get(); | 
|  | shouldThrow(); | 
|  | } catch (ExecutionException success) { | 
|  | assertSame(t, success.getCause()); | 
|  | } catch (Throwable fail) { threadUnexpectedException(fail); } | 
|  |  | 
|  | try { | 
|  | f.get(5L, SECONDS); | 
|  | shouldThrow(); | 
|  | } catch (ExecutionException success) { | 
|  | assertSame(t, success.getCause()); | 
|  | } catch (Throwable fail) { threadUnexpectedException(fail); } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Subclass to expose protected methods | 
|  | */ | 
|  | static class PublicFutureTask extends FutureTask { | 
|  | private final AtomicInteger runCount; | 
|  | private final AtomicInteger doneCount = new AtomicInteger(0); | 
|  | private final AtomicInteger runAndResetCount = new AtomicInteger(0); | 
|  | private final AtomicInteger setCount = new AtomicInteger(0); | 
|  | private final AtomicInteger setExceptionCount = new AtomicInteger(0); | 
|  | public int runCount() { return runCount.get(); } | 
|  | public int doneCount() { return doneCount.get(); } | 
|  | public int runAndResetCount() { return runAndResetCount.get(); } | 
|  | public int setCount() { return setCount.get(); } | 
|  | public int setExceptionCount() { return setExceptionCount.get(); } | 
|  |  | 
|  | PublicFutureTask(Runnable runnable) { | 
|  | this(runnable, seven); | 
|  | } | 
|  | PublicFutureTask(Runnable runnable, Object result) { | 
|  | this(runnable, result, new AtomicInteger(0)); | 
|  | } | 
|  | private PublicFutureTask(final Runnable runnable, Object result, | 
|  | final AtomicInteger runCount) { | 
|  | super(new Runnable() { | 
|  | public void run() { | 
|  | runCount.getAndIncrement(); | 
|  | runnable.run(); | 
|  | }}, result); | 
|  | this.runCount = runCount; | 
|  | } | 
|  | PublicFutureTask(Callable callable) { | 
|  | this(callable, new AtomicInteger(0)); | 
|  | } | 
|  | private PublicFutureTask(final Callable callable, | 
|  | final AtomicInteger runCount) { | 
|  | super(new Callable() { | 
|  | public Object call() throws Exception { | 
|  | runCount.getAndIncrement(); | 
|  | return callable.call(); | 
|  | }}); | 
|  | this.runCount = runCount; | 
|  | } | 
|  | @Override public void done() { | 
|  | assertTrue(isDone()); | 
|  | doneCount.incrementAndGet(); | 
|  | super.done(); | 
|  | } | 
|  | @Override public boolean runAndReset() { | 
|  | runAndResetCount.incrementAndGet(); | 
|  | return super.runAndReset(); | 
|  | } | 
|  | @Override public void set(Object x) { | 
|  | setCount.incrementAndGet(); | 
|  | super.set(x); | 
|  | } | 
|  | @Override public void setException(Throwable t) { | 
|  | setExceptionCount.incrementAndGet(); | 
|  | super.setException(t); | 
|  | } | 
|  | } | 
|  |  | 
|  | class Counter extends CheckedRunnable { | 
|  | final AtomicInteger count = new AtomicInteger(0); | 
|  | public int get() { return count.get(); } | 
|  | public void realRun() { | 
|  | count.getAndIncrement(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * creating a future with a null callable throws NullPointerException | 
|  | */ | 
|  | public void testConstructor() { | 
|  | try { | 
|  | new FutureTask(null); | 
|  | shouldThrow(); | 
|  | } catch (NullPointerException success) {} | 
|  | } | 
|  |  | 
|  | /** | 
|  | * creating a future with null runnable throws NullPointerException | 
|  | */ | 
|  | public void testConstructor2() { | 
|  | try { | 
|  | new FutureTask(null, Boolean.TRUE); | 
|  | shouldThrow(); | 
|  | } catch (NullPointerException success) {} | 
|  | } | 
|  |  | 
|  | /** | 
|  | * isDone is true when a task completes | 
|  | */ | 
|  | public void testIsDone() { | 
|  | PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); | 
|  | assertFalse(task.isDone()); | 
|  | task.run(); | 
|  | assertTrue(task.isDone()); | 
|  | checkCompletedNormally(task, Boolean.TRUE); | 
|  | assertEquals(1, task.runCount()); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * runAndReset of a non-cancelled task succeeds | 
|  | */ | 
|  | public void testRunAndReset() { | 
|  | PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); | 
|  | for (int i = 0; i < 3; i++) { | 
|  | assertTrue(task.runAndReset()); | 
|  | checkNotDone(task); | 
|  | assertEquals(i + 1, task.runCount()); | 
|  | assertEquals(i + 1, task.runAndResetCount()); | 
|  | assertEquals(0, task.setCount()); | 
|  | assertEquals(0, task.setExceptionCount()); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * runAndReset after cancellation fails | 
|  | */ | 
|  | public void testRunAndResetAfterCancel() { | 
|  | for (boolean mayInterruptIfRunning : new boolean[] { true, false }) { | 
|  | PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); | 
|  | assertTrue(task.cancel(mayInterruptIfRunning)); | 
|  | for (int i = 0; i < 3; i++) { | 
|  | assertFalse(task.runAndReset()); | 
|  | assertEquals(0, task.runCount()); | 
|  | assertEquals(i + 1, task.runAndResetCount()); | 
|  | assertEquals(0, task.setCount()); | 
|  | assertEquals(0, task.setExceptionCount()); | 
|  | } | 
|  | tryToConfuseDoneTask(task); | 
|  | checkCancelled(task); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * setting value causes get to return it | 
|  | */ | 
|  | public void testSet() throws Exception { | 
|  | PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); | 
|  | task.set(one); | 
|  | for (int i = 0; i < 3; i++) { | 
|  | assertSame(one, task.get()); | 
|  | assertSame(one, task.get(LONG_DELAY_MS, MILLISECONDS)); | 
|  | assertEquals(1, task.setCount()); | 
|  | } | 
|  | tryToConfuseDoneTask(task); | 
|  | checkCompletedNormally(task, one); | 
|  | assertEquals(0, task.runCount()); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * setException causes get to throw ExecutionException | 
|  | */ | 
|  | public void testSetException_get() throws Exception { | 
|  | Exception nse = new NoSuchElementException(); | 
|  | PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); | 
|  | task.setException(nse); | 
|  |  | 
|  | try { | 
|  | task.get(); | 
|  | shouldThrow(); | 
|  | } catch (ExecutionException success) { | 
|  | assertSame(nse, success.getCause()); | 
|  | checkCompletedAbnormally(task, nse); | 
|  | } | 
|  |  | 
|  | try { | 
|  | task.get(LONG_DELAY_MS, MILLISECONDS); | 
|  | shouldThrow(); | 
|  | } catch (ExecutionException success) { | 
|  | assertSame(nse, success.getCause()); | 
|  | checkCompletedAbnormally(task, nse); | 
|  | } | 
|  |  | 
|  | assertEquals(1, task.setExceptionCount()); | 
|  | assertEquals(0, task.setCount()); | 
|  | tryToConfuseDoneTask(task); | 
|  | checkCompletedAbnormally(task, nse); | 
|  | assertEquals(0, task.runCount()); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * cancel(false) before run succeeds | 
|  | */ | 
|  | public void testCancelBeforeRun() { | 
|  | PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); | 
|  | assertTrue(task.cancel(false)); | 
|  | task.run(); | 
|  | assertEquals(0, task.runCount()); | 
|  | assertEquals(0, task.setCount()); | 
|  | assertEquals(0, task.setExceptionCount()); | 
|  | assertTrue(task.isCancelled()); | 
|  | assertTrue(task.isDone()); | 
|  | tryToConfuseDoneTask(task); | 
|  | assertEquals(0, task.runCount()); | 
|  | checkCancelled(task); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * cancel(true) before run succeeds | 
|  | */ | 
|  | public void testCancelBeforeRun2() { | 
|  | PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); | 
|  | assertTrue(task.cancel(true)); | 
|  | task.run(); | 
|  | assertEquals(0, task.runCount()); | 
|  | assertEquals(0, task.setCount()); | 
|  | assertEquals(0, task.setExceptionCount()); | 
|  | assertTrue(task.isCancelled()); | 
|  | assertTrue(task.isDone()); | 
|  | tryToConfuseDoneTask(task); | 
|  | assertEquals(0, task.runCount()); | 
|  | checkCancelled(task); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * cancel(false) of a completed task fails | 
|  | */ | 
|  | public void testCancelAfterRun() { | 
|  | PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); | 
|  | task.run(); | 
|  | assertFalse(task.cancel(false)); | 
|  | assertEquals(1, task.runCount()); | 
|  | assertEquals(1, task.setCount()); | 
|  | assertEquals(0, task.setExceptionCount()); | 
|  | tryToConfuseDoneTask(task); | 
|  | checkCompletedNormally(task, Boolean.TRUE); | 
|  | assertEquals(1, task.runCount()); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * cancel(true) of a completed task fails | 
|  | */ | 
|  | public void testCancelAfterRun2() { | 
|  | PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); | 
|  | task.run(); | 
|  | assertFalse(task.cancel(true)); | 
|  | assertEquals(1, task.runCount()); | 
|  | assertEquals(1, task.setCount()); | 
|  | assertEquals(0, task.setExceptionCount()); | 
|  | tryToConfuseDoneTask(task); | 
|  | checkCompletedNormally(task, Boolean.TRUE); | 
|  | assertEquals(1, task.runCount()); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * cancel(true) interrupts a running task that subsequently succeeds | 
|  | */ | 
|  | public void testCancelInterrupt() { | 
|  | final CountDownLatch pleaseCancel = new CountDownLatch(1); | 
|  | final PublicFutureTask task = | 
|  | new PublicFutureTask(new CheckedRunnable() { | 
|  | public void realRun() { | 
|  | pleaseCancel.countDown(); | 
|  | try { | 
|  | delay(LONG_DELAY_MS); | 
|  | shouldThrow(); | 
|  | } catch (InterruptedException success) {} | 
|  | }}); | 
|  |  | 
|  | Thread t = newStartedThread(task); | 
|  | await(pleaseCancel); | 
|  | assertTrue(task.cancel(true)); | 
|  | assertTrue(task.isCancelled()); | 
|  | assertTrue(task.isDone()); | 
|  | awaitTermination(t); | 
|  | assertEquals(1, task.runCount()); | 
|  | assertEquals(1, task.setCount()); | 
|  | assertEquals(0, task.setExceptionCount()); | 
|  | tryToConfuseDoneTask(task); | 
|  | checkCancelled(task); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * cancel(true) tries to interrupt a running task, but | 
|  | * Thread.interrupt throws (simulating a restrictive security | 
|  | * manager) | 
|  | */ | 
|  | public void testCancelInterrupt_ThrowsSecurityException() { | 
|  | final CountDownLatch pleaseCancel = new CountDownLatch(1); | 
|  | final CountDownLatch cancelled = new CountDownLatch(1); | 
|  | final PublicFutureTask task = | 
|  | new PublicFutureTask(new CheckedRunnable() { | 
|  | public void realRun() { | 
|  | pleaseCancel.countDown(); | 
|  | await(cancelled); | 
|  | assertFalse(Thread.interrupted()); | 
|  | }}); | 
|  |  | 
|  | final Thread t = new Thread(task) { | 
|  | // Simulate a restrictive security manager. | 
|  | @Override public void interrupt() { | 
|  | throw new SecurityException(); | 
|  | }}; | 
|  | t.setDaemon(true); | 
|  | t.start(); | 
|  |  | 
|  | await(pleaseCancel); | 
|  | try { | 
|  | task.cancel(true); | 
|  | shouldThrow(); | 
|  | } catch (SecurityException expected) {} | 
|  |  | 
|  | // We failed to deliver the interrupt, but the world retains | 
|  | // its sanity, as if we had done task.cancel(false) | 
|  | assertTrue(task.isCancelled()); | 
|  | assertTrue(task.isDone()); | 
|  | assertEquals(1, task.runCount()); | 
|  | assertEquals(1, task.doneCount()); | 
|  | assertEquals(0, task.setCount()); | 
|  | assertEquals(0, task.setExceptionCount()); | 
|  | cancelled.countDown(); | 
|  | awaitTermination(t); | 
|  | assertEquals(1, task.setCount()); | 
|  | assertEquals(0, task.setExceptionCount()); | 
|  | tryToConfuseDoneTask(task); | 
|  | checkCancelled(task); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * cancel(true) interrupts a running task that subsequently throws | 
|  | */ | 
|  | public void testCancelInterrupt_taskFails() { | 
|  | final CountDownLatch pleaseCancel = new CountDownLatch(1); | 
|  | final PublicFutureTask task = | 
|  | new PublicFutureTask(new Runnable() { | 
|  | public void run() { | 
|  | pleaseCancel.countDown(); | 
|  | try { | 
|  | delay(LONG_DELAY_MS); | 
|  | threadShouldThrow(); | 
|  | } catch (InterruptedException success) { | 
|  | } catch (Throwable t) { threadUnexpectedException(t); } | 
|  | throw new RuntimeException(); | 
|  | }}); | 
|  |  | 
|  | Thread t = newStartedThread(task); | 
|  | await(pleaseCancel); | 
|  | assertTrue(task.cancel(true)); | 
|  | assertTrue(task.isCancelled()); | 
|  | awaitTermination(t); | 
|  | assertEquals(1, task.runCount()); | 
|  | assertEquals(0, task.setCount()); | 
|  | assertEquals(1, task.setExceptionCount()); | 
|  | tryToConfuseDoneTask(task); | 
|  | checkCancelled(task); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * cancel(false) does not interrupt a running task | 
|  | */ | 
|  | public void testCancelNoInterrupt() { | 
|  | final CountDownLatch pleaseCancel = new CountDownLatch(1); | 
|  | final CountDownLatch cancelled = new CountDownLatch(1); | 
|  | final PublicFutureTask task = | 
|  | new PublicFutureTask(new CheckedCallable<Boolean>() { | 
|  | public Boolean realCall() { | 
|  | pleaseCancel.countDown(); | 
|  | await(cancelled); | 
|  | assertFalse(Thread.interrupted()); | 
|  | return Boolean.TRUE; | 
|  | }}); | 
|  |  | 
|  | Thread t = newStartedThread(task); | 
|  | await(pleaseCancel); | 
|  | assertTrue(task.cancel(false)); | 
|  | assertTrue(task.isCancelled()); | 
|  | cancelled.countDown(); | 
|  | awaitTermination(t); | 
|  | assertEquals(1, task.runCount()); | 
|  | assertEquals(1, task.setCount()); | 
|  | assertEquals(0, task.setExceptionCount()); | 
|  | tryToConfuseDoneTask(task); | 
|  | checkCancelled(task); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * run in one thread causes get in another thread to retrieve value | 
|  | */ | 
|  | public void testGetRun() { | 
|  | final CountDownLatch pleaseRun = new CountDownLatch(2); | 
|  |  | 
|  | final PublicFutureTask task = | 
|  | new PublicFutureTask(new CheckedCallable<Object>() { | 
|  | public Object realCall() { | 
|  | return two; | 
|  | }}); | 
|  |  | 
|  | Thread t1 = newStartedThread(new CheckedRunnable() { | 
|  | public void realRun() throws Exception { | 
|  | pleaseRun.countDown(); | 
|  | assertSame(two, task.get()); | 
|  | }}); | 
|  |  | 
|  | Thread t2 = newStartedThread(new CheckedRunnable() { | 
|  | public void realRun() throws Exception { | 
|  | pleaseRun.countDown(); | 
|  | assertSame(two, task.get(2*LONG_DELAY_MS, MILLISECONDS)); | 
|  | }}); | 
|  |  | 
|  | await(pleaseRun); | 
|  | checkNotDone(task); | 
|  | assertTrue(t1.isAlive()); | 
|  | assertTrue(t2.isAlive()); | 
|  | task.run(); | 
|  | checkCompletedNormally(task, two); | 
|  | assertEquals(1, task.runCount()); | 
|  | assertEquals(1, task.setCount()); | 
|  | assertEquals(0, task.setExceptionCount()); | 
|  | awaitTermination(t1); | 
|  | awaitTermination(t2); | 
|  | tryToConfuseDoneTask(task); | 
|  | checkCompletedNormally(task, two); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * set in one thread causes get in another thread to retrieve value | 
|  | */ | 
|  | public void testGetSet() { | 
|  | final CountDownLatch pleaseSet = new CountDownLatch(2); | 
|  |  | 
|  | final PublicFutureTask task = | 
|  | new PublicFutureTask(new CheckedCallable<Object>() { | 
|  | public Object realCall() throws InterruptedException { | 
|  | return two; | 
|  | }}); | 
|  |  | 
|  | Thread t1 = newStartedThread(new CheckedRunnable() { | 
|  | public void realRun() throws Exception { | 
|  | pleaseSet.countDown(); | 
|  | assertSame(two, task.get()); | 
|  | }}); | 
|  |  | 
|  | Thread t2 = newStartedThread(new CheckedRunnable() { | 
|  | public void realRun() throws Exception { | 
|  | pleaseSet.countDown(); | 
|  | assertSame(two, task.get(2*LONG_DELAY_MS, MILLISECONDS)); | 
|  | }}); | 
|  |  | 
|  | await(pleaseSet); | 
|  | checkNotDone(task); | 
|  | assertTrue(t1.isAlive()); | 
|  | assertTrue(t2.isAlive()); | 
|  | task.set(two); | 
|  | assertEquals(0, task.runCount()); | 
|  | assertEquals(1, task.setCount()); | 
|  | assertEquals(0, task.setExceptionCount()); | 
|  | tryToConfuseDoneTask(task); | 
|  | checkCompletedNormally(task, two); | 
|  | awaitTermination(t1); | 
|  | awaitTermination(t2); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Cancelling a task causes timed get in another thread to throw | 
|  | * CancellationException | 
|  | */ | 
|  | public void testTimedGet_Cancellation() { | 
|  | testTimedGet_Cancellation(false); | 
|  | } | 
|  | public void testTimedGet_Cancellation_interrupt() { | 
|  | testTimedGet_Cancellation(true); | 
|  | } | 
|  | public void testTimedGet_Cancellation(final boolean mayInterruptIfRunning) { | 
|  | final CountDownLatch pleaseCancel = new CountDownLatch(3); | 
|  | final CountDownLatch cancelled = new CountDownLatch(1); | 
|  | final Callable<Object> callable = | 
|  | new CheckedCallable<Object>() { | 
|  | public Object realCall() throws InterruptedException { | 
|  | pleaseCancel.countDown(); | 
|  | if (mayInterruptIfRunning) { | 
|  | try { | 
|  | delay(2*LONG_DELAY_MS); | 
|  | } catch (InterruptedException success) {} | 
|  | } else { | 
|  | await(cancelled); | 
|  | } | 
|  | return two; | 
|  | }}; | 
|  | final PublicFutureTask task = new PublicFutureTask(callable); | 
|  |  | 
|  | Thread t1 = new ThreadShouldThrow(CancellationException.class) { | 
|  | public void realRun() throws Exception { | 
|  | pleaseCancel.countDown(); | 
|  | task.get(); | 
|  | }}; | 
|  | Thread t2 = new ThreadShouldThrow(CancellationException.class) { | 
|  | public void realRun() throws Exception { | 
|  | pleaseCancel.countDown(); | 
|  | task.get(2*LONG_DELAY_MS, MILLISECONDS); | 
|  | }}; | 
|  | t1.start(); | 
|  | t2.start(); | 
|  | Thread t3 = newStartedThread(task); | 
|  | await(pleaseCancel); | 
|  | checkIsRunning(task); | 
|  | task.cancel(mayInterruptIfRunning); | 
|  | checkCancelled(task); | 
|  | awaitTermination(t1); | 
|  | awaitTermination(t2); | 
|  | cancelled.countDown(); | 
|  | awaitTermination(t3); | 
|  | assertEquals(1, task.runCount()); | 
|  | assertEquals(1, task.setCount()); | 
|  | assertEquals(0, task.setExceptionCount()); | 
|  | tryToConfuseDoneTask(task); | 
|  | checkCancelled(task); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * A runtime exception in task causes get to throw ExecutionException | 
|  | */ | 
|  | public void testGet_ExecutionException() throws InterruptedException { | 
|  | final ArithmeticException e = new ArithmeticException(); | 
|  | final PublicFutureTask task = new PublicFutureTask(new Callable() { | 
|  | public Object call() { | 
|  | throw e; | 
|  | }}); | 
|  |  | 
|  | task.run(); | 
|  | assertEquals(1, task.runCount()); | 
|  | assertEquals(0, task.setCount()); | 
|  | assertEquals(1, task.setExceptionCount()); | 
|  | try { | 
|  | task.get(); | 
|  | shouldThrow(); | 
|  | } catch (ExecutionException success) { | 
|  | assertSame(e, success.getCause()); | 
|  | tryToConfuseDoneTask(task); | 
|  | checkCompletedAbnormally(task, success.getCause()); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * A runtime exception in task causes timed get to throw ExecutionException | 
|  | */ | 
|  | public void testTimedGet_ExecutionException2() throws Exception { | 
|  | final ArithmeticException e = new ArithmeticException(); | 
|  | final PublicFutureTask task = new PublicFutureTask(new Callable() { | 
|  | public Object call() { | 
|  | throw e; | 
|  | }}); | 
|  |  | 
|  | task.run(); | 
|  | try { | 
|  | task.get(LONG_DELAY_MS, MILLISECONDS); | 
|  | shouldThrow(); | 
|  | } catch (ExecutionException success) { | 
|  | assertSame(e, success.getCause()); | 
|  | tryToConfuseDoneTask(task); | 
|  | checkCompletedAbnormally(task, success.getCause()); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * get is interruptible | 
|  | */ | 
|  | public void testGet_interruptible() { | 
|  | final CountDownLatch pleaseInterrupt = new CountDownLatch(1); | 
|  | final FutureTask task = new FutureTask(new NoOpCallable()); | 
|  | Thread t = newStartedThread(new CheckedRunnable() { | 
|  | public void realRun() throws Exception { | 
|  | Thread.currentThread().interrupt(); | 
|  | try { | 
|  | task.get(); | 
|  | shouldThrow(); | 
|  | } catch (InterruptedException success) {} | 
|  | assertFalse(Thread.interrupted()); | 
|  |  | 
|  | pleaseInterrupt.countDown(); | 
|  | try { | 
|  | task.get(); | 
|  | shouldThrow(); | 
|  | } catch (InterruptedException success) {} | 
|  | assertFalse(Thread.interrupted()); | 
|  | }}); | 
|  |  | 
|  | await(pleaseInterrupt); | 
|  | t.interrupt(); | 
|  | awaitTermination(t); | 
|  | checkNotDone(task); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * timed get is interruptible | 
|  | */ | 
|  | public void testTimedGet_interruptible() { | 
|  | final CountDownLatch pleaseInterrupt = new CountDownLatch(1); | 
|  | final FutureTask task = new FutureTask(new NoOpCallable()); | 
|  | Thread t = newStartedThread(new CheckedRunnable() { | 
|  | public void realRun() throws Exception { | 
|  | Thread.currentThread().interrupt(); | 
|  | try { | 
|  | task.get(2*LONG_DELAY_MS, MILLISECONDS); | 
|  | shouldThrow(); | 
|  | } catch (InterruptedException success) {} | 
|  | assertFalse(Thread.interrupted()); | 
|  |  | 
|  | pleaseInterrupt.countDown(); | 
|  | try { | 
|  | task.get(2*LONG_DELAY_MS, MILLISECONDS); | 
|  | shouldThrow(); | 
|  | } catch (InterruptedException success) {} | 
|  | assertFalse(Thread.interrupted()); | 
|  | }}); | 
|  |  | 
|  | await(pleaseInterrupt); | 
|  | t.interrupt(); | 
|  | awaitTermination(t); | 
|  | checkNotDone(task); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * A timed out timed get throws TimeoutException | 
|  | */ | 
|  | public void testGet_TimeoutException() throws Exception { | 
|  | FutureTask task = new FutureTask(new NoOpCallable()); | 
|  | long startTime = System.nanoTime(); | 
|  | try { | 
|  | task.get(timeoutMillis(), MILLISECONDS); | 
|  | shouldThrow(); | 
|  | } catch (TimeoutException success) { | 
|  | assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * timed get with null TimeUnit throws NullPointerException | 
|  | */ | 
|  | public void testGet_NullTimeUnit() throws Exception { | 
|  | FutureTask task = new FutureTask(new NoOpCallable()); | 
|  | long[] timeouts = { Long.MIN_VALUE, 0L, Long.MAX_VALUE }; | 
|  |  | 
|  | for (long timeout : timeouts) { | 
|  | try { | 
|  | task.get(timeout, null); | 
|  | shouldThrow(); | 
|  | } catch (NullPointerException success) {} | 
|  | } | 
|  |  | 
|  | task.run(); | 
|  |  | 
|  | for (long timeout : timeouts) { | 
|  | try { | 
|  | task.get(timeout, null); | 
|  | shouldThrow(); | 
|  | } catch (NullPointerException success) {} | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * timed get with most negative timeout works correctly (i.e. no | 
|  | * underflow bug) | 
|  | */ | 
|  | public void testGet_NegativeInfinityTimeout() throws Exception { | 
|  | final ExecutorService pool = Executors.newFixedThreadPool(10); | 
|  | final Runnable nop = new Runnable() { public void run() {}}; | 
|  | final FutureTask<Void> task = new FutureTask<>(nop, null); | 
|  | final List<Future<?>> futures = new ArrayList<>(); | 
|  | Runnable r = new Runnable() { public void run() { | 
|  | for (long timeout : new long[] { 0L, -1L, Long.MIN_VALUE }) { | 
|  | try { | 
|  | task.get(timeout, NANOSECONDS); | 
|  | shouldThrow(); | 
|  | } catch (TimeoutException success) { | 
|  | } catch (Throwable fail) {threadUnexpectedException(fail);}}}}; | 
|  | for (int i = 0; i < 10; i++) | 
|  | futures.add(pool.submit(r)); | 
|  | try { | 
|  | joinPool(pool); | 
|  | for (Future<?> future : futures) | 
|  | checkCompletedNormally(future, null); | 
|  | } finally { | 
|  | task.run();         // last resort to help terminate | 
|  | } | 
|  | } | 
|  |  | 
|  | } |