| /* |
| * 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 remains |
| // consistent, 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 |
| } |
| } |
| |
| } |