--- jsr166/src/test/tck/FutureTaskTest.java 2012/12/20 04:58:53 1.33 +++ jsr166/src/test/tck/FutureTaskTest.java 2021/01/26 13:33:06 1.57 @@ -6,24 +6,30 @@ * Pat Fisher, Mike Judd. */ -import junit.framework.*; -import java.security.Permission; +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 static java.util.concurrent.TimeUnit.MILLISECONDS; -import static java.util.concurrent.TimeUnit.SECONDS; -import java.util.*; + +import junit.framework.Test; +import junit.framework.TestSuite; public class FutureTaskTest extends JSR166TestCase { public static void main(String[] args) { - junit.textui.TestRunner.run(suite()); + main(suite(), args); } public static Test suite() { return new TestSuite(FutureTaskTest.class); @@ -38,16 +44,34 @@ public class FutureTaskTest extends JSR1 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(); - int savedSetCount = pf.setCount(); - int savedSetExceptionCount = pf.setExceptionCount(); pf.run(); pf.runAndReset(); assertEquals(savedRunCount, pf.runCount()); - assertEquals(savedSetCount, pf.setCount()); - assertEquals(savedSetExceptionCount, pf.setExceptionCount()); + 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()); } } @@ -66,25 +90,31 @@ public class FutureTaskTest extends JSR1 void checkIsRunning(Future f) { checkNotDone(f); if (f instanceof FutureTask) { - FutureTask ft = (FutureTask) f; + FutureTask ft = (FutureTask) f; // Check that run methods do nothing ft.run(); - if (f instanceof PublicFutureTask) - assertFalse(((PublicFutureTask) f).runAndReset()); + if (f instanceof PublicFutureTask) { + PublicFutureTask pf = (PublicFutureTask) f; + int savedRunCount = pf.runCount(); + pf.run(); + assertFalse(pf.runAndReset()); + assertEquals(savedRunCount, pf.runCount()); + } checkNotDone(f); } } - void checkCompletedNormally(Future f, T expected) { + void checkCompletedNormally(Future f, T expectedValue) { checkIsDone(f); assertFalse(f.isCancelled()); + T v1 = null, v2 = null; try { - assertSame(expected, f.get()); - } catch (Throwable fail) { threadUnexpectedException(fail); } - try { - assertSame(expected, f.get(5L, SECONDS)); + v1 = f.get(); + v2 = f.get(randomTimeout(), randomTimeUnit()); } catch (Throwable fail) { threadUnexpectedException(fail); } + assertSame(expectedValue, v1); + assertSame(expectedValue, v2); } void checkCancelled(Future f) { @@ -98,7 +128,7 @@ public class FutureTaskTest extends JSR1 } catch (Throwable fail) { threadUnexpectedException(fail); } try { - f.get(5L, SECONDS); + f.get(randomTimeout(), randomTimeUnit()); shouldThrow(); } catch (CancellationException success) { } catch (Throwable fail) { threadUnexpectedException(fail); } @@ -108,7 +138,7 @@ public class FutureTaskTest extends JSR1 pf.set(new Object()); pf.setException(new Error()); for (boolean mayInterruptIfRunning : new boolean[] { true, false }) { - pf.cancel(true); + pf.cancel(mayInterruptIfRunning); } } @@ -124,7 +154,7 @@ public class FutureTaskTest extends JSR1 } catch (Throwable fail) { threadUnexpectedException(fail); } try { - f.get(5L, SECONDS); + f.get(randomTimeout(), randomTimeUnit()); shouldThrow(); } catch (ExecutionException success) { assertSame(t, success.getCause()); @@ -134,7 +164,7 @@ public class FutureTaskTest extends JSR1 /** * Subclass to expose protected methods */ - static class PublicFutureTask extends FutureTask { + static class PublicFutureTask extends FutureTask { private final AtomicInteger runCount; private final AtomicInteger doneCount = new AtomicInteger(0); private final AtomicInteger runAndResetCount = new AtomicInteger(0); @@ -161,12 +191,12 @@ public class FutureTaskTest extends JSR1 }}, result); this.runCount = runCount; } - PublicFutureTask(Callable callable) { + PublicFutureTask(Callable callable) { this(callable, new AtomicInteger(0)); } - private PublicFutureTask(final Callable callable, + private PublicFutureTask(final Callable callable, final AtomicInteger runCount) { - super(new Callable() { + super(new Callable() { public Object call() throws Exception { runCount.getAndIncrement(); return callable.call(); @@ -205,7 +235,7 @@ public class FutureTaskTest extends JSR1 */ public void testConstructor() { try { - new FutureTask(null); + new FutureTask(null); shouldThrow(); } catch (NullPointerException success) {} } @@ -215,7 +245,7 @@ public class FutureTaskTest extends JSR1 */ public void testConstructor2() { try { - new FutureTask(null, Boolean.TRUE); + new FutureTask(null, Boolean.TRUE); shouldThrow(); } catch (NullPointerException success) {} } @@ -240,8 +270,8 @@ public class FutureTaskTest extends JSR1 for (int i = 0; i < 3; i++) { assertTrue(task.runAndReset()); checkNotDone(task); - assertEquals(i+1, task.runCount()); - assertEquals(i+1, task.runAndResetCount()); + assertEquals(i + 1, task.runCount()); + assertEquals(i + 1, task.runAndResetCount()); assertEquals(0, task.setCount()); assertEquals(0, task.setExceptionCount()); } @@ -257,7 +287,7 @@ public class FutureTaskTest extends JSR1 for (int i = 0; i < 3; i++) { assertFalse(task.runAndReset()); assertEquals(0, task.runCount()); - assertEquals(i+1, task.runAndResetCount()); + assertEquals(i + 1, task.runAndResetCount()); assertEquals(0, task.setCount()); assertEquals(0, task.setExceptionCount()); } @@ -390,6 +420,7 @@ public class FutureTaskTest extends JSR1 delay(LONG_DELAY_MS); shouldThrow(); } catch (InterruptedException success) {} + assertFalse(Thread.interrupted()); }}); Thread t = newStartedThread(task); @@ -433,7 +464,7 @@ public class FutureTaskTest extends JSR1 try { task.cancel(true); shouldThrow(); - } catch (SecurityException expected) {} + } catch (SecurityException success) {} // We failed to deliver the interrupt, but the world retains // its sanity, as if we had done task.cancel(false) @@ -459,10 +490,13 @@ public class FutureTaskTest extends JSR1 final PublicFutureTask task = new PublicFutureTask(new Runnable() { public void run() { + pleaseCancel.countDown(); try { - pleaseCancel.countDown(); delay(LONG_DELAY_MS); - } finally { throw new RuntimeException(); } + threadShouldThrow(); + } catch (InterruptedException success) { + } catch (Throwable t) { threadUnexpectedException(t); } + throw new RuntimeException(); }}); Thread t = newStartedThread(task); @@ -587,51 +621,55 @@ public class FutureTaskTest extends JSR1 * CancellationException */ public void testTimedGet_Cancellation() { - for (final boolean mayInterruptIfRunning : - new boolean[] { true, false }) { - final CountDownLatch pleaseCancel = new CountDownLatch(3); - final CountDownLatch cancelled = new CountDownLatch(1); - final PublicFutureTask task = - new PublicFutureTask(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; - }}); + 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 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) { + Thread t1 = new ThreadShouldThrow(CancellationException.class) { public void realRun() throws Exception { pleaseCancel.countDown(); task.get(); }}; - Thread t2 = new ThreadShouldThrow(CancellationException.class) { + 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); - } + 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); } /** @@ -639,7 +677,7 @@ public class FutureTaskTest extends JSR1 */ public void testGet_ExecutionException() throws InterruptedException { final ArithmeticException e = new ArithmeticException(); - final PublicFutureTask task = new PublicFutureTask(new Callable() { + final PublicFutureTask task = new PublicFutureTask(new Callable() { public Object call() { throw e; }}); @@ -663,7 +701,7 @@ public class FutureTaskTest extends JSR1 */ public void testTimedGet_ExecutionException2() throws Exception { final ArithmeticException e = new ArithmeticException(); - final PublicFutureTask task = new PublicFutureTask(new Callable() { + final PublicFutureTask task = new PublicFutureTask(new Callable() { public Object call() { throw e; }}); @@ -682,9 +720,9 @@ public class FutureTaskTest extends JSR1 /** * get is interruptible */ - public void testGet_interruptible() { + public void testGet_Interruptible() { final CountDownLatch pleaseInterrupt = new CountDownLatch(1); - final FutureTask task = new FutureTask(new NoOpCallable()); + final FutureTask task = new FutureTask(new NoOpCallable()); Thread t = newStartedThread(new CheckedRunnable() { public void realRun() throws Exception { Thread.currentThread().interrupt(); @@ -711,27 +749,28 @@ public class FutureTaskTest extends JSR1 /** * timed get is interruptible */ - public void testTimedGet_interruptible() { + public void testTimedGet_Interruptible() { final CountDownLatch pleaseInterrupt = new CountDownLatch(1); - final FutureTask task = new FutureTask(new NoOpCallable()); + 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); + task.get(randomTimeout(), randomTimeUnit()); shouldThrow(); } catch (InterruptedException success) {} assertFalse(Thread.interrupted()); pleaseInterrupt.countDown(); try { - task.get(2*LONG_DELAY_MS, MILLISECONDS); + 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); @@ -741,7 +780,7 @@ public class FutureTaskTest extends JSR1 * A timed out timed get throws TimeoutException */ public void testGet_TimeoutException() throws Exception { - FutureTask task = new FutureTask(new NoOpCallable()); + FutureTask task = new FutureTask(new NoOpCallable()); long startTime = System.nanoTime(); try { task.get(timeoutMillis(), MILLISECONDS); @@ -755,7 +794,7 @@ public class FutureTaskTest extends JSR1 * timed get with null TimeUnit throws NullPointerException */ public void testGet_NullTimeUnit() throws Exception { - FutureTask task = new FutureTask(new NoOpCallable()); + FutureTask task = new FutureTask(new NoOpCallable()); long[] timeouts = { Long.MIN_VALUE, 0L, Long.MAX_VALUE }; for (long timeout : timeouts) { @@ -775,4 +814,72 @@ public class FutureTaskTest extends JSR1 } } + /** + * 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 task = new FutureTask<>(nop, null); + final List> 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 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 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 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 f = new FutureTask<>(() -> ""); + assertTrue(f.cancel(mayInterruptIfRunning)); + assertTrue(f.toString().matches(".*\\[.*Cancelled.*\\]")); + if (testImplementationDetails) + assertEquals(identityString(f) + "[Cancelled]", + f.toString()); + } + } + }