--- jsr166/src/test/tck/JSR166TestCase.java 2015/09/07 17:14:06 1.140 +++ jsr166/src/test/tck/JSR166TestCase.java 2015/10/04 05:52:08 1.163 @@ -7,6 +7,7 @@ */ import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.NANOSECONDS; import java.io.ByteArrayInputStream; @@ -15,6 +16,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -40,6 +42,7 @@ import java.util.concurrent.CyclicBarrie import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; +import java.util.concurrent.ForkJoinPool; import java.util.concurrent.Future; import java.util.concurrent.RecursiveAction; import java.util.concurrent.RecursiveTask; @@ -67,18 +70,18 @@ import junit.framework.TestSuite; * *
    * - *
  1. All assertions in code running in generated threads must use + *
  2. All assertions in code running in generated threads must use * the forms {@link #threadFail}, {@link #threadAssertTrue}, {@link * #threadAssertEquals}, or {@link #threadAssertNull}, (not * {@code fail}, {@code assertTrue}, etc.) It is OK (but not * particularly recommended) for other code to use these forms too. * Only the most typically used JUnit assertion methods are defined - * this way, but enough to live with.
  3. + * this way, but enough to live with. * - *
  4. If you override {@link #setUp} or {@link #tearDown}, make sure + *
  5. If you override {@link #setUp} or {@link #tearDown}, make sure * to invoke {@code super.setUp} and {@code super.tearDown} within * them. These methods are used to clear and check for thread - * assertion failures.
  6. + * assertion failures. * *
  7. All delays and timeouts must use one of the constants {@code * SHORT_DELAY_MS}, {@code SMALL_DELAY_MS}, {@code MEDIUM_DELAY_MS}, @@ -89,44 +92,44 @@ import junit.framework.TestSuite; * is always discriminable as larger than SHORT and smaller than * MEDIUM. And so on. These constants are set to conservative values, * but even so, if there is ever any doubt, they can all be increased - * in one spot to rerun tests on slower platforms.
  8. + * in one spot to rerun tests on slower platforms. * - *
  9. All threads generated must be joined inside each test case + *
  10. All threads generated must be joined inside each test case * method (or {@code fail} to do so) before returning from the * method. The {@code joinPool} method can be used to do this when - * using Executors.
  11. + * using Executors. * *
* *

Other notes *

*/ @@ -185,27 +188,53 @@ public class JSR166TestCase extends Test return (regex == null) ? null : Pattern.compile(regex); } - protected void runTest() throws Throwable { + static volatile TestCase currentTestCase; + static { + Runnable checkForWedgedTest = new Runnable() { public void run() { + // avoid spurious reports with enormous runsPerTest + final int timeoutMinutes = Math.max(runsPerTest / 10, 1); + for (TestCase lastTestCase = currentTestCase;;) { + try { MINUTES.sleep(timeoutMinutes); } + catch (InterruptedException unexpected) { break; } + if (lastTestCase == currentTestCase) { + System.err.println + ("Looks like we're stuck running test: " + + lastTestCase); + dumpTestThreads(); + } + lastTestCase = currentTestCase; + }}}; + Thread thread = new Thread(checkForWedgedTest, "checkForWedgedTest"); + thread.setDaemon(true); + thread.start(); + } + + public void runBare() throws Throwable { + currentTestCase = this; if (methodFilter == null - || methodFilter.matcher(toString()).find()) { - for (int i = 0; i < runsPerTest; i++) { - if (profileTests) - runTestProfiled(); - else - super.runTest(); - } + || methodFilter.matcher(toString()).find()) + super.runBare(); + } + + protected void runTest() throws Throwable { + for (int i = 0; i < runsPerTest; i++) { + if (profileTests) + runTestProfiled(); + else + super.runTest(); } } protected void runTestProfiled() throws Throwable { - // Warmup run, notably to trigger all needed classloading. - super.runTest(); - long t0 = System.nanoTime(); - try { + for (int i = 0; i < 2; i++) { + long startTime = System.nanoTime(); super.runTest(); - } finally { - long elapsedMillis = millisElapsedSince(t0); - if (elapsedMillis >= profileThreshold) + long elapsedMillis = millisElapsedSince(startTime); + if (elapsedMillis < profileThreshold) + break; + // Never report first run of any test; treat it as a + // warmup run, notably to trigger all needed classloading, + if (i > 0) System.out.printf("%n%s: %d%n", toString(), elapsedMillis); } } @@ -388,7 +417,7 @@ public class JSR166TestCase extends Test // Java9+ test classes if (atLeastJava9()) { String[] java9TestClassNames = { - // Currently empty + // Currently empty, but expecting varhandle tests }; addNamedTestClasses(suite, java9TestClassNames); } @@ -455,7 +484,6 @@ public class JSR166TestCase extends Test } else { return new TestSuite(); } - } // Delays for timing-dependent tests, in milliseconds. @@ -513,6 +541,8 @@ public class JSR166TestCase extends Test * the same test have no effect. */ public void threadRecordFailure(Throwable t) { + System.err.println(t); + dumpTestThreads(); threadFailure.compareAndSet(null, t); } @@ -520,6 +550,13 @@ public class JSR166TestCase extends Test setDelays(); } + void tearDownFail(String format, Object... args) { + String msg = toString() + ": " + String.format(format, args); + System.err.println(msg); + dumpTestThreads(); + throw new AssertionFailedError(msg); + } + /** * Extra checks that get done for all test cases. * @@ -547,7 +584,7 @@ public class JSR166TestCase extends Test } if (Thread.interrupted()) - throw new AssertionFailedError("interrupt status set in main thread"); + tearDownFail("interrupt status set in main thread"); checkForkJoinPoolThreadLeaks(); } @@ -556,7 +593,7 @@ public class JSR166TestCase extends Test * Finds missing try { ... } finally { joinPool(e); } */ void checkForkJoinPoolThreadLeaks() throws InterruptedException { - Thread[] survivors = new Thread[5]; + Thread[] survivors = new Thread[7]; int count = Thread.enumerate(survivors); for (int i = 0; i < count; i++) { Thread thread = survivors[i]; @@ -564,12 +601,15 @@ public class JSR166TestCase extends Test if (name.startsWith("ForkJoinPool-")) { // give thread some time to terminate thread.join(LONG_DELAY_MS); - if (!thread.isAlive()) continue; - throw new AssertionFailedError - (String.format("Found leaked ForkJoinPool thread test=%s thread=%s%n", - toString(), name)); + if (thread.isAlive()) + tearDownFail("Found leaked ForkJoinPool thread thread=%s", + thread); } } + + if (!ForkJoinPool.commonPool() + .awaitQuiescence(LONG_DELAY_MS, MILLISECONDS)) + tearDownFail("ForkJoin common pool thread stuck"); } /** @@ -582,7 +622,7 @@ public class JSR166TestCase extends Test fail(reason); } catch (AssertionFailedError t) { threadRecordFailure(t); - fail(reason); + throw t; } } @@ -709,22 +749,33 @@ public class JSR166TestCase extends Test /** * Delays, via Thread.sleep, for the given millisecond delay, but * if the sleep is shorter than specified, may re-sleep or yield - * until time elapses. + * until time elapses. Ensures that the given time, as measured + * by System.nanoTime(), has elapsed. */ static void delay(long millis) throws InterruptedException { - long startTime = System.nanoTime(); - long ns = millis * 1000 * 1000; - for (;;) { + long nanos = millis * (1000 * 1000); + final long wakeupTime = System.nanoTime() + nanos; + do { if (millis > 0L) Thread.sleep(millis); else // too short to sleep Thread.yield(); - long d = ns - (System.nanoTime() - startTime); - if (d > 0L) - millis = d / (1000 * 1000); - else - break; - } + nanos = wakeupTime - System.nanoTime(); + millis = nanos / (1000 * 1000); + } while (nanos >= 0L); + } + + /** + * Allows use of try-with-resources with per-test thread pools. + */ + class PoolCleaner implements AutoCloseable { + private final ExecutorService pool; + public PoolCleaner(ExecutorService pool) { this.pool = pool; } + public void close() { joinPool(pool); } + } + + PoolCleaner cleaner(ExecutorService pool) { + return new PoolCleaner(pool); } /** @@ -733,31 +784,38 @@ public class JSR166TestCase extends Test void joinPool(ExecutorService pool) { try { pool.shutdown(); - if (!pool.awaitTermination(2 * LONG_DELAY_MS, MILLISECONDS)) - fail("ExecutorService " + pool + - " did not terminate in a timely manner"); + if (!pool.awaitTermination(2 * LONG_DELAY_MS, MILLISECONDS)) { + try { + threadFail("ExecutorService " + pool + + " did not terminate in a timely manner"); + } finally { + // last resort, for the benefit of subsequent tests + pool.shutdownNow(); + pool.awaitTermination(SMALL_DELAY_MS, MILLISECONDS); + } + } } catch (SecurityException ok) { // Allowed in case test doesn't have privs } catch (InterruptedException fail) { - fail("Unexpected InterruptedException"); + threadFail("Unexpected InterruptedException"); } } /** Like Runnable, but with the freedom to throw anything */ - interface Thunk { public void run() throws Throwable; } + interface Action { public void run() throws Throwable; } /** - * Runs all the given tasks in parallel, failing if any fail. + * Runs all the given actions in parallel, failing if any fail. * Useful for running multiple variants of tests that are * necessarily individually slow because they must block. */ - void testInParallel(Thunk ... thunks) { + void testInParallel(Action ... actions) { ExecutorService pool = Executors.newCachedThreadPool(); - try { - ArrayList> futures = new ArrayList<>(thunks.length); - for (final Thunk thunk : thunks) + try (PoolCleaner cleaner = cleaner(pool)) { + ArrayList> futures = new ArrayList<>(actions.length); + for (final Action action : actions) futures.add(pool.submit(new CheckedRunnable() { - public void realRun() throws Throwable { thunk.run();}})); + public void realRun() throws Throwable { action.run();}})); for (Future future : futures) try { assertNull(future.get(LONG_DELAY_MS, MILLISECONDS)); @@ -766,19 +824,31 @@ public class JSR166TestCase extends Test } catch (Exception ex) { threadUnexpectedException(ex); } - } finally { - joinPool(pool); } } /** - * A debugging tool to print all stack traces, as jstack does. + * A debugging tool to print stack traces of most threads, as jstack does. + * Uninteresting threads are filtered out. */ - static void printAllStackTraces() { - for (ThreadInfo info : - ManagementFactory.getThreadMXBean() - .dumpAllThreads(true, true)) + static void dumpTestThreads() { + ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); + System.err.println("------ stacktrace dump start ------"); + for (ThreadInfo info : threadMXBean.dumpAllThreads(true, true)) { + String name = info.getThreadName(); + if ("Signal Dispatcher".equals(name)) + continue; + if ("Reference Handler".equals(name) + && info.getLockName().startsWith("java.lang.ref.Reference$Lock")) + continue; + if ("Finalizer".equals(name) + && info.getLockName().startsWith("java.lang.ref.ReferenceQueue$Lock")) + continue; + if ("checkForWedgedTest".equals(name)) + continue; System.err.print(info); + } + System.err.println("------ stacktrace dump end ------"); } /** @@ -798,7 +868,7 @@ public class JSR166TestCase extends Test delay(millis); assertTrue(thread.isAlive()); } catch (InterruptedException fail) { - fail("Unexpected InterruptedException"); + threadFail("Unexpected InterruptedException"); } } @@ -820,7 +890,7 @@ public class JSR166TestCase extends Test for (Thread thread : threads) assertTrue(thread.isAlive()); } catch (InterruptedException fail) { - fail("Unexpected InterruptedException"); + threadFail("Unexpected InterruptedException"); } } @@ -1223,7 +1293,10 @@ public class JSR166TestCase extends Test public static final String TEST_STRING = "a test string"; public static class StringTask implements Callable { - public String call() { return TEST_STRING; } + final String value; + public StringTask() { this(TEST_STRING); } + public StringTask(String value) { this.value = value; } + public String call() { return value; } } public Callable latchAwaitingStringTask(final CountDownLatch latch) { @@ -1236,13 +1309,31 @@ public class JSR166TestCase extends Test }}; } - public Runnable awaiter(final CountDownLatch latch) { + public Runnable countDowner(final CountDownLatch latch) { return new CheckedRunnable() { public void realRun() throws InterruptedException { - await(latch); + latch.countDown(); }}; } + class LatchAwaiter extends CheckedRunnable { + static final int NEW = 0; + static final int RUNNING = 1; + static final int DONE = 2; + final CountDownLatch latch; + int state = NEW; + LatchAwaiter(CountDownLatch latch) { this.latch = latch; } + public void realRun() throws InterruptedException { + state = 1; + await(latch); + state = 2; + } + } + + public LatchAwaiter awaiter(CountDownLatch latch) { + return new LatchAwaiter(latch); + } + public void await(CountDownLatch latch) { try { assertTrue(latch.await(LONG_DELAY_MS, MILLISECONDS));