summaryrefslogtreecommitdiffstats
path: root/jsr166-tests/src/test/java/jsr166/FutureTaskTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'jsr166-tests/src/test/java/jsr166/FutureTaskTest.java')
-rw-r--r--jsr166-tests/src/test/java/jsr166/FutureTaskTest.java799
1 files changed, 799 insertions, 0 deletions
diff --git a/jsr166-tests/src/test/java/jsr166/FutureTaskTest.java b/jsr166-tests/src/test/java/jsr166/FutureTaskTest.java
new file mode 100644
index 0000000..baab79e
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/FutureTaskTest.java
@@ -0,0 +1,799 @@
+/*
+ * 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 junit.framework.*;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import java.util.*;
+
+public class FutureTaskTest extends JSR166TestCase {
+
+ 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(true);
+ }
+ }
+
+ 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() {
+ try {
+ pleaseCancel.countDown();
+ 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) {}
+ }
+ }
+
+}