| /* | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| /* | |
| * This file is available under and governed by the GNU General Public | |
| * License version 2 only, as published by the Free Software Foundation. | |
| * However, the following notice accompanied the original version of this | |
| * file: | |
| * | |
| * 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. | |
| */ | |
| import static java.util.concurrent.TimeUnit.MILLISECONDS; | |
| import static java.util.concurrent.TimeUnit.NANOSECONDS; | |
| 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 { | |
| 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()); | |
| Object r2 = null; | |
| try { | |
| r2 = f.get(); | |
| } catch (CancellationException t) { | |
| assertSame(exInfo, CancellationException.class); | |
| } catch (ExecutionException t) { | |
| assertSame(exInfo, t.getCause()); | |
| } catch (Throwable t) { | |
| threadUnexpectedException(t); | |
| } | |
| if (exInfo == null) | |
| assertSame(r, r2); | |
| 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 expectedValue) { | |
| checkIsDone(f); | |
| assertFalse(f.isCancelled()); | |
| T v1 = null, v2 = null; | |
| try { | |
| v1 = f.get(); | |
| v2 = f.get(randomTimeout(), randomTimeUnit()); | |
| } catch (Throwable fail) { threadUnexpectedException(fail); } | |
| assertSame(expectedValue, v1); | |
| assertSame(expectedValue, v2); | |
| } | |
| void checkCancelled(Future<?> f) { | |
| checkIsDone(f); | |
| assertTrue(f.isCancelled()); | |
| try { | |
| f.get(); | |
| shouldThrow(); | |
| } catch (CancellationException success) { | |
| } catch (Throwable fail) { threadUnexpectedException(fail); } | |
| try { | |
| f.get(randomTimeout(), randomTimeUnit()); | |
| 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(randomTimeout(), randomTimeUnit()); | |
| shouldThrow(); | |
| } catch (ExecutionException success) { | |
| assertSame(t, success.getCause()); | |
| } catch (Throwable fail) { threadUnexpectedException(fail); } | |
| } | |
| /** | |
| * Subclass to expose protected methods | |
| */ | |
| static class PublicFutureTask extends FutureTask<Object> { | |
| 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<Object>() { | |
| 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<Void>(null); | |
| shouldThrow(); | |
| } catch (NullPointerException success) {} | |
| } | |
| /** | |
| * creating a future with null runnable throws NullPointerException | |
| */ | |
| public void testConstructor2() { | |
| try { | |
| new FutureTask<Boolean>(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) {} | |
| assertFalse(Thread.interrupted()); | |
| }}); | |
| 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) 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<>() { | |
| 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<Object>() { | |
| 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<Object>() { | |
| 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<Object> 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<Object> task = new FutureTask<>(new NoOpCallable()); | |
| Thread t = newStartedThread(new CheckedRunnable() { | |
| public void realRun() throws Exception { | |
| Thread.currentThread().interrupt(); | |
| try { | |
| task.get(randomTimeout(), randomTimeUnit()); | |
| shouldThrow(); | |
| } catch (InterruptedException success) {} | |
| assertFalse(Thread.interrupted()); | |
| pleaseInterrupt.countDown(); | |
| try { | |
| task.get(LONGER_DELAY_MS, MILLISECONDS); | |
| shouldThrow(); | |
| } catch (InterruptedException success) {} | |
| assertFalse(Thread.interrupted()); | |
| }}); | |
| await(pleaseInterrupt); | |
| if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING); | |
| t.interrupt(); | |
| awaitTermination(t); | |
| checkNotDone(task); | |
| } | |
| /** | |
| * A timed out timed get throws TimeoutException | |
| */ | |
| public void testGet_TimeoutException() throws Exception { | |
| FutureTask<Object> 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<Object> 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 | |
| } | |
| } | |
| /** | |
| * toString indicates current completion state | |
| */ | |
| public void testToString_incomplete() { | |
| FutureTask<String> f = new FutureTask<>(() -> ""); | |
| assertTrue(f.toString().matches(".*\\[.*Not completed.*\\]")); | |
| if (testImplementationDetails) | |
| assertTrue(f.toString().startsWith( | |
| identityString(f) + "[Not completed, task =")); | |
| } | |
| public void testToString_normal() { | |
| FutureTask<String> f = new FutureTask<>(() -> ""); | |
| f.run(); | |
| assertTrue(f.toString().matches(".*\\[.*Completed normally.*\\]")); | |
| if (testImplementationDetails) | |
| assertEquals(identityString(f) + "[Completed normally]", | |
| f.toString()); | |
| } | |
| public void testToString_exception() { | |
| FutureTask<String> f = new FutureTask<>( | |
| () -> { throw new ArithmeticException(); }); | |
| f.run(); | |
| assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]")); | |
| if (testImplementationDetails) | |
| assertTrue(f.toString().startsWith( | |
| identityString(f) + "[Completed exceptionally: ")); | |
| } | |
| public void testToString_cancelled() { | |
| for (boolean mayInterruptIfRunning : new boolean[] { true, false }) { | |
| FutureTask<String> f = new FutureTask<>(() -> ""); | |
| assertTrue(f.cancel(mayInterruptIfRunning)); | |
| assertTrue(f.toString().matches(".*\\[.*Cancelled.*\\]")); | |
| if (testImplementationDetails) | |
| assertEquals(identityString(f) + "[Cancelled]", | |
| f.toString()); | |
| } | |
| } | |
| } |