ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/tck/JSR166TestCase.java
(Generate patch)

Comparing jsr166/src/test/tck/JSR166TestCase.java (file contents):
Revision 1.141 by jsr166, Tue Sep 8 16:49:16 2015 UTC vs.
Revision 1.184 by jsr166, Wed Feb 10 00:05:20 2016 UTC

# Line 6 | Line 6
6   * Pat Fisher, Mike Judd.
7   */
8  
9 + /*
10 + * @test
11 + * @summary JSR-166 tck tests
12 + * @modules java.management
13 + * @build *
14 + * @run junit/othervm/timeout=1000 -Djsr166.testImplementationDetails=true JSR166TestCase
15 + */
16 +
17   import static java.util.concurrent.TimeUnit.MILLISECONDS;
18 + import static java.util.concurrent.TimeUnit.MINUTES;
19   import static java.util.concurrent.TimeUnit.NANOSECONDS;
20  
21   import java.io.ByteArrayInputStream;
# Line 15 | Line 24 | import java.io.ObjectInputStream;
24   import java.io.ObjectOutputStream;
25   import java.lang.management.ManagementFactory;
26   import java.lang.management.ThreadInfo;
27 + import java.lang.management.ThreadMXBean;
28   import java.lang.reflect.Constructor;
29   import java.lang.reflect.Method;
30   import java.lang.reflect.Modifier;
31 + import java.nio.file.Files;
32 + import java.nio.file.Paths;
33   import java.security.CodeSource;
34   import java.security.Permission;
35   import java.security.PermissionCollection;
# Line 40 | Line 52 | import java.util.concurrent.CyclicBarrie
52   import java.util.concurrent.ExecutionException;
53   import java.util.concurrent.Executors;
54   import java.util.concurrent.ExecutorService;
55 + import java.util.concurrent.ForkJoinPool;
56   import java.util.concurrent.Future;
57   import java.util.concurrent.RecursiveAction;
58   import java.util.concurrent.RecursiveTask;
# Line 49 | Line 62 | import java.util.concurrent.ThreadFactor
62   import java.util.concurrent.ThreadPoolExecutor;
63   import java.util.concurrent.TimeoutException;
64   import java.util.concurrent.atomic.AtomicReference;
65 + import java.util.regex.Matcher;
66   import java.util.regex.Pattern;
67  
68   import junit.framework.AssertionFailedError;
# Line 67 | Line 81 | import junit.framework.TestSuite;
81   *
82   * <ol>
83   *
84 < * <li> All assertions in code running in generated threads must use
84 > * <li>All assertions in code running in generated threads must use
85   * the forms {@link #threadFail}, {@link #threadAssertTrue}, {@link
86   * #threadAssertEquals}, or {@link #threadAssertNull}, (not
87   * {@code fail}, {@code assertTrue}, etc.) It is OK (but not
88   * particularly recommended) for other code to use these forms too.
89   * Only the most typically used JUnit assertion methods are defined
90 < * this way, but enough to live with.</li>
90 > * this way, but enough to live with.
91   *
92 < * <li> If you override {@link #setUp} or {@link #tearDown}, make sure
92 > * <li>If you override {@link #setUp} or {@link #tearDown}, make sure
93   * to invoke {@code super.setUp} and {@code super.tearDown} within
94   * them. These methods are used to clear and check for thread
95 < * assertion failures.</li>
95 > * assertion failures.
96   *
97   * <li>All delays and timeouts must use one of the constants {@code
98   * SHORT_DELAY_MS}, {@code SMALL_DELAY_MS}, {@code MEDIUM_DELAY_MS},
# Line 89 | Line 103 | import junit.framework.TestSuite;
103   * is always discriminable as larger than SHORT and smaller than
104   * MEDIUM.  And so on. These constants are set to conservative values,
105   * but even so, if there is ever any doubt, they can all be increased
106 < * in one spot to rerun tests on slower platforms.</li>
106 > * in one spot to rerun tests on slower platforms.
107   *
108 < * <li> All threads generated must be joined inside each test case
108 > * <li>All threads generated must be joined inside each test case
109   * method (or {@code fail} to do so) before returning from the
110   * method. The {@code joinPool} method can be used to do this when
111 < * using Executors.</li>
111 > * using Executors.
112   *
113   * </ol>
114   *
115   * <p><b>Other notes</b>
116   * <ul>
117   *
118 < * <li> Usually, there is one testcase method per JSR166 method
118 > * <li>Usually, there is one testcase method per JSR166 method
119   * covering "normal" operation, and then as many exception-testing
120   * methods as there are exceptions the method can throw. Sometimes
121   * there are multiple tests per JSR166 method when the different
122   * "normal" behaviors differ significantly. And sometimes testcases
123 < * cover multiple methods when they cannot be tested in
110 < * isolation.</li>
123 > * cover multiple methods when they cannot be tested in isolation.
124   *
125 < * <li> The documentation style for testcases is to provide as javadoc
125 > * <li>The documentation style for testcases is to provide as javadoc
126   * a simple sentence or two describing the property that the testcase
127   * method purports to test. The javadocs do not say anything about how
128 < * the property is tested. To find out, read the code.</li>
128 > * the property is tested. To find out, read the code.
129   *
130 < * <li> These tests are "conformance tests", and do not attempt to
130 > * <li>These tests are "conformance tests", and do not attempt to
131   * test throughput, latency, scalability or other performance factors
132   * (see the separate "jtreg" tests for a set intended to check these
133   * for the most central aspects of functionality.) So, most tests use
134   * the smallest sensible numbers of threads, collection sizes, etc
135 < * needed to check basic conformance.</li>
135 > * needed to check basic conformance.
136   *
137   * <li>The test classes currently do not declare inclusion in
138   * any particular package to simplify things for people integrating
139 < * them in TCK test suites.</li>
139 > * them in TCK test suites.
140   *
141 < * <li> As a convenience, the {@code main} of this class (JSR166TestCase)
142 < * runs all JSR166 unit tests.</li>
141 > * <li>As a convenience, the {@code main} of this class (JSR166TestCase)
142 > * runs all JSR166 unit tests.
143   *
144   * </ul>
145   */
# Line 170 | Line 183 | public class JSR166TestCase extends Test
183      private static final int suiteRuns =
184          Integer.getInteger("jsr166.suiteRuns", 1);
185  
186 +    /**
187 +     * The scaling factor to apply to standard delays used in tests.
188 +     */
189 +    private static final int delayFactor =
190 +        Integer.getInteger("jsr166.delay.factor", 1);
191 +
192      public JSR166TestCase() { super(); }
193      public JSR166TestCase(String name) { super(name); }
194  
# Line 185 | Line 204 | public class JSR166TestCase extends Test
204          return (regex == null) ? null : Pattern.compile(regex);
205      }
206  
207 <    protected void runTest() throws Throwable {
207 >    // Instrumentation to debug very rare, but very annoying hung test runs.
208 >    static volatile TestCase currentTestCase;
209 >    // static volatile int currentRun = 0;
210 >    static {
211 >        Runnable checkForWedgedTest = new Runnable() { public void run() {
212 >            // Avoid spurious reports with enormous runsPerTest.
213 >            // A single test case run should never take more than 1 second.
214 >            // But let's cap it at the high end too ...
215 >            final int timeoutMinutes =
216 >                Math.min(15, Math.max(runsPerTest / 60, 1));
217 >            for (TestCase lastTestCase = currentTestCase;;) {
218 >                try { MINUTES.sleep(timeoutMinutes); }
219 >                catch (InterruptedException unexpected) { break; }
220 >                if (lastTestCase == currentTestCase) {
221 >                    System.err.printf(
222 >                        "Looks like we're stuck running test: %s%n",
223 >                        lastTestCase);
224 > //                     System.err.printf(
225 > //                         "Looks like we're stuck running test: %s (%d/%d)%n",
226 > //                         lastTestCase, currentRun, runsPerTest);
227 > //                     System.err.println("availableProcessors=" +
228 > //                         Runtime.getRuntime().availableProcessors());
229 > //                     System.err.printf("cpu model = %s%n", cpuModel());
230 >                    dumpTestThreads();
231 >                    // one stack dump is probably enough; more would be spam
232 >                    break;
233 >                }
234 >                lastTestCase = currentTestCase;
235 >            }}};
236 >        Thread thread = new Thread(checkForWedgedTest, "checkForWedgedTest");
237 >        thread.setDaemon(true);
238 >        thread.start();
239 >    }
240 >
241 > //     public static String cpuModel() {
242 > //         try {
243 > //             Matcher matcher = Pattern.compile("model name\\s*: (.*)")
244 > //                 .matcher(new String(
245 > //                      Files.readAllBytes(Paths.get("/proc/cpuinfo")), "UTF-8"));
246 > //             matcher.find();
247 > //             return matcher.group(1);
248 > //         } catch (Exception ex) { return null; }
249 > //     }
250 >
251 >    public void runBare() throws Throwable {
252 >        currentTestCase = this;
253          if (methodFilter == null
254 <            || methodFilter.matcher(toString()).find()) {
255 <            for (int i = 0; i < runsPerTest; i++) {
256 <                if (profileTests)
257 <                    runTestProfiled();
258 <                else
259 <                    super.runTest();
260 <            }
254 >            || methodFilter.matcher(toString()).find())
255 >            super.runBare();
256 >    }
257 >
258 >    protected void runTest() throws Throwable {
259 >        for (int i = 0; i < runsPerTest; i++) {
260 >            // currentRun = i;
261 >            if (profileTests)
262 >                runTestProfiled();
263 >            else
264 >                super.runTest();
265          }
266      }
267  
268      protected void runTestProfiled() throws Throwable {
269 <        // Warmup run, notably to trigger all needed classloading.
270 <        super.runTest();
203 <        long t0 = System.nanoTime();
204 <        try {
269 >        for (int i = 0; i < 2; i++) {
270 >            long startTime = System.nanoTime();
271              super.runTest();
272 <        } finally {
273 <            long elapsedMillis = millisElapsedSince(t0);
274 <            if (elapsedMillis >= profileThreshold)
272 >            long elapsedMillis = millisElapsedSince(startTime);
273 >            if (elapsedMillis < profileThreshold)
274 >                break;
275 >            // Never report first run of any test; treat it as a
276 >            // warmup run, notably to trigger all needed classloading,
277 >            if (i > 0)
278                  System.out.printf("%n%s: %d%n", toString(), elapsedMillis);
279          }
280      }
# Line 217 | Line 286 | public class JSR166TestCase extends Test
286          main(suite(), args);
287      }
288  
289 +    static class PithyResultPrinter extends junit.textui.ResultPrinter {
290 +        PithyResultPrinter(java.io.PrintStream writer) { super(writer); }
291 +        long runTime;
292 +        public void startTest(Test test) {}
293 +        protected void printHeader(long runTime) {
294 +            this.runTime = runTime; // defer printing for later
295 +        }
296 +        protected void printFooter(TestResult result) {
297 +            if (result.wasSuccessful()) {
298 +                getWriter().println("OK (" + result.runCount() + " tests)"
299 +                    + "  Time: " + elapsedTimeAsString(runTime));
300 +            } else {
301 +                getWriter().println("Time: " + elapsedTimeAsString(runTime));
302 +                super.printFooter(result);
303 +            }
304 +        }
305 +    }
306 +
307 +    /**
308 +     * Returns a TestRunner that doesn't bother with unnecessary
309 +     * fluff, like printing a "." for each test case.
310 +     */
311 +    static junit.textui.TestRunner newPithyTestRunner() {
312 +        junit.textui.TestRunner runner = new junit.textui.TestRunner();
313 +        runner.setPrinter(new PithyResultPrinter(System.out));
314 +        return runner;
315 +    }
316 +
317      /**
318       * Runs all unit tests in the given test suite.
319       * Actual behavior influenced by jsr166.* system properties.
# Line 228 | Line 325 | public class JSR166TestCase extends Test
325              System.setSecurityManager(new SecurityManager());
326          }
327          for (int i = 0; i < suiteRuns; i++) {
328 <            TestResult result = junit.textui.TestRunner.run(suite);
328 >            TestResult result = newPithyTestRunner().doRun(suite);
329              if (!result.wasSuccessful())
330                  System.exit(1);
331              System.gc();
# Line 388 | Line 485 | public class JSR166TestCase extends Test
485          // Java9+ test classes
486          if (atLeastJava9()) {
487              String[] java9TestClassNames = {
488 <                // Currently empty
488 >                // Currently empty, but expecting varhandle tests
489              };
490              addNamedTestClasses(suite, java9TestClassNames);
491          }
# Line 455 | Line 552 | public class JSR166TestCase extends Test
552          } else {
553              return new TestSuite();
554          }
458
555      }
556  
557      // Delays for timing-dependent tests, in milliseconds.
# Line 466 | Line 562 | public class JSR166TestCase extends Test
562      public static long LONG_DELAY_MS;
563  
564      /**
565 <     * Returns the shortest timed delay. This could
566 <     * be reimplemented to use for example a Property.
565 >     * Returns the shortest timed delay. This can be scaled up for
566 >     * slow machines using the jsr166.delay.factor system property.
567       */
568      protected long getShortDelay() {
569 <        return 50;
569 >        return 50 * delayFactor;
570      }
571  
572      /**
# Line 513 | Line 609 | public class JSR166TestCase extends Test
609       * the same test have no effect.
610       */
611      public void threadRecordFailure(Throwable t) {
612 +        System.err.println(t);
613 +        dumpTestThreads();
614          threadFailure.compareAndSet(null, t);
615      }
616  
# Line 520 | Line 618 | public class JSR166TestCase extends Test
618          setDelays();
619      }
620  
621 +    void tearDownFail(String format, Object... args) {
622 +        String msg = toString() + ": " + String.format(format, args);
623 +        System.err.println(msg);
624 +        dumpTestThreads();
625 +        throw new AssertionFailedError(msg);
626 +    }
627 +
628      /**
629       * Extra checks that get done for all test cases.
630       *
# Line 547 | Line 652 | public class JSR166TestCase extends Test
652          }
653  
654          if (Thread.interrupted())
655 <            throw new AssertionFailedError("interrupt status set in main thread");
655 >            tearDownFail("interrupt status set in main thread");
656  
657          checkForkJoinPoolThreadLeaks();
658      }
659  
660      /**
661 <     * Finds missing try { ... } finally { joinPool(e); }
661 >     * Finds missing PoolCleaners
662       */
663      void checkForkJoinPoolThreadLeaks() throws InterruptedException {
664 <        Thread[] survivors = new Thread[5];
664 >        Thread[] survivors = new Thread[7];
665          int count = Thread.enumerate(survivors);
666          for (int i = 0; i < count; i++) {
667              Thread thread = survivors[i];
# Line 564 | Line 669 | public class JSR166TestCase extends Test
669              if (name.startsWith("ForkJoinPool-")) {
670                  // give thread some time to terminate
671                  thread.join(LONG_DELAY_MS);
672 <                if (!thread.isAlive()) continue;
673 <                throw new AssertionFailedError
674 <                    (String.format("Found leaked ForkJoinPool thread test=%s thread=%s%n",
570 <                                   toString(), name));
672 >                if (thread.isAlive())
673 >                    tearDownFail("Found leaked ForkJoinPool thread thread=%s",
674 >                                 thread);
675              }
676          }
677 +
678 +        if (!ForkJoinPool.commonPool()
679 +            .awaitQuiescence(LONG_DELAY_MS, MILLISECONDS))
680 +            tearDownFail("ForkJoin common pool thread stuck");
681      }
682  
683      /**
# Line 582 | Line 690 | public class JSR166TestCase extends Test
690              fail(reason);
691          } catch (AssertionFailedError t) {
692              threadRecordFailure(t);
693 <            fail(reason);
693 >            throw t;
694          }
695      }
696  
# Line 709 | Line 817 | public class JSR166TestCase extends Test
817      /**
818       * Delays, via Thread.sleep, for the given millisecond delay, but
819       * if the sleep is shorter than specified, may re-sleep or yield
820 <     * until time elapses.
820 >     * until time elapses.  Ensures that the given time, as measured
821 >     * by System.nanoTime(), has elapsed.
822       */
823      static void delay(long millis) throws InterruptedException {
824 <        long startTime = System.nanoTime();
825 <        long ns = millis * 1000 * 1000;
826 <        for (;;) {
824 >        long nanos = millis * (1000 * 1000);
825 >        final long wakeupTime = System.nanoTime() + nanos;
826 >        do {
827              if (millis > 0L)
828                  Thread.sleep(millis);
829              else // too short to sleep
830                  Thread.yield();
831 <            long d = ns - (System.nanoTime() - startTime);
832 <            if (d > 0L)
833 <                millis = d / (1000 * 1000);
834 <            else
835 <                break;
831 >            nanos = wakeupTime - System.nanoTime();
832 >            millis = nanos / (1000 * 1000);
833 >        } while (nanos >= 0L);
834 >    }
835 >
836 >    /**
837 >     * Allows use of try-with-resources with per-test thread pools.
838 >     */
839 >    class PoolCleaner implements AutoCloseable {
840 >        private final ExecutorService pool;
841 >        public PoolCleaner(ExecutorService pool) { this.pool = pool; }
842 >        public void close() { joinPool(pool); }
843 >    }
844 >
845 >    /**
846 >     * An extension of PoolCleaner that has an action to release the pool.
847 >     */
848 >    class PoolCleanerWithReleaser extends PoolCleaner {
849 >        private final Runnable releaser;
850 >        public PoolCleanerWithReleaser(ExecutorService pool, Runnable releaser) {
851 >            super(pool);
852 >            this.releaser = releaser;
853          }
854 +        public void close() {
855 +            try {
856 +                releaser.run();
857 +            } finally {
858 +                super.close();
859 +            }
860 +        }
861 +    }
862 +
863 +    PoolCleaner cleaner(ExecutorService pool) {
864 +        return new PoolCleaner(pool);
865 +    }
866 +
867 +    PoolCleaner cleaner(ExecutorService pool, Runnable releaser) {
868 +        return new PoolCleanerWithReleaser(pool, releaser);
869 +    }
870 +
871 +    PoolCleaner cleaner(ExecutorService pool, CountDownLatch latch) {
872 +        return new PoolCleanerWithReleaser(pool, releaser(latch));
873 +    }
874 +
875 +    Runnable releaser(final CountDownLatch latch) {
876 +        return new Runnable() { public void run() {
877 +            do { latch.countDown(); }
878 +            while (latch.getCount() > 0);
879 +        }};
880      }
881  
882      /**
# Line 733 | Line 885 | public class JSR166TestCase extends Test
885      void joinPool(ExecutorService pool) {
886          try {
887              pool.shutdown();
888 <            if (!pool.awaitTermination(2 * LONG_DELAY_MS, MILLISECONDS))
889 <                fail("ExecutorService " + pool +
890 <                     " did not terminate in a timely manner");
888 >            if (!pool.awaitTermination(2 * LONG_DELAY_MS, MILLISECONDS)) {
889 >                try {
890 >                    threadFail("ExecutorService " + pool +
891 >                               " did not terminate in a timely manner");
892 >                } finally {
893 >                    // last resort, for the benefit of subsequent tests
894 >                    pool.shutdownNow();
895 >                    pool.awaitTermination(MEDIUM_DELAY_MS, MILLISECONDS);
896 >                }
897 >            }
898          } catch (SecurityException ok) {
899              // Allowed in case test doesn't have privs
900          } catch (InterruptedException fail) {
901 <            fail("Unexpected InterruptedException");
901 >            threadFail("Unexpected InterruptedException");
902          }
903      }
904  
# Line 753 | Line 912 | public class JSR166TestCase extends Test
912       */
913      void testInParallel(Action ... actions) {
914          ExecutorService pool = Executors.newCachedThreadPool();
915 <        try {
915 >        try (PoolCleaner cleaner = cleaner(pool)) {
916              ArrayList<Future<?>> futures = new ArrayList<>(actions.length);
917              for (final Action action : actions)
918                  futures.add(pool.submit(new CheckedRunnable() {
# Line 766 | Line 925 | public class JSR166TestCase extends Test
925                  } catch (Exception ex) {
926                      threadUnexpectedException(ex);
927                  }
769        } finally {
770            joinPool(pool);
928          }
929      }
930  
931      /**
932 <     * A debugging tool to print all stack traces, as jstack does.
932 >     * A debugging tool to print stack traces of most threads, as jstack does.
933 >     * Uninteresting threads are filtered out.
934       */
935 <    static void printAllStackTraces() {
936 <        for (ThreadInfo info :
937 <                 ManagementFactory.getThreadMXBean()
938 <                 .dumpAllThreads(true, true))
935 >    static void dumpTestThreads() {
936 >        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
937 >        System.err.println("------ stacktrace dump start ------");
938 >        for (ThreadInfo info : threadMXBean.dumpAllThreads(true, true)) {
939 >            String name = info.getThreadName();
940 >            if ("Signal Dispatcher".equals(name))
941 >                continue;
942 >            if ("Reference Handler".equals(name)
943 >                && info.getLockName().startsWith("java.lang.ref.Reference$Lock"))
944 >                continue;
945 >            if ("Finalizer".equals(name)
946 >                && info.getLockName().startsWith("java.lang.ref.ReferenceQueue$Lock"))
947 >                continue;
948 >            if ("checkForWedgedTest".equals(name))
949 >                continue;
950              System.err.print(info);
951 +        }
952 +        System.err.println("------ stacktrace dump end ------");
953      }
954  
955      /**
# Line 798 | Line 969 | public class JSR166TestCase extends Test
969              delay(millis);
970              assertTrue(thread.isAlive());
971          } catch (InterruptedException fail) {
972 <            fail("Unexpected InterruptedException");
972 >            threadFail("Unexpected InterruptedException");
973          }
974      }
975  
# Line 820 | Line 991 | public class JSR166TestCase extends Test
991              for (Thread thread : threads)
992                  assertTrue(thread.isAlive());
993          } catch (InterruptedException fail) {
994 <            fail("Unexpected InterruptedException");
994 >            threadFail("Unexpected InterruptedException");
995          }
996      }
997  
# Line 1098 | Line 1269 | public class JSR166TestCase extends Test
1269          } finally {
1270              if (t.getState() != Thread.State.TERMINATED) {
1271                  t.interrupt();
1272 <                fail("Test timed out");
1272 >                threadFail("timed out waiting for thread to terminate");
1273              }
1274          }
1275      }
# Line 1223 | Line 1394 | public class JSR166TestCase extends Test
1394      public static final String TEST_STRING = "a test string";
1395  
1396      public static class StringTask implements Callable<String> {
1397 <        public String call() { return TEST_STRING; }
1397 >        final String value;
1398 >        public StringTask() { this(TEST_STRING); }
1399 >        public StringTask(String value) { this.value = value; }
1400 >        public String call() { return value; }
1401      }
1402  
1403      public Callable<String> latchAwaitingStringTask(final CountDownLatch latch) {
# Line 1236 | Line 1410 | public class JSR166TestCase extends Test
1410              }};
1411      }
1412  
1413 <    public Runnable awaiter(final CountDownLatch latch) {
1413 >    public Runnable countDowner(final CountDownLatch latch) {
1414          return new CheckedRunnable() {
1415              public void realRun() throws InterruptedException {
1416 <                await(latch);
1416 >                latch.countDown();
1417              }};
1418      }
1419  
1420 +    class LatchAwaiter extends CheckedRunnable {
1421 +        static final int NEW = 0;
1422 +        static final int RUNNING = 1;
1423 +        static final int DONE = 2;
1424 +        final CountDownLatch latch;
1425 +        int state = NEW;
1426 +        LatchAwaiter(CountDownLatch latch) { this.latch = latch; }
1427 +        public void realRun() throws InterruptedException {
1428 +            state = 1;
1429 +            await(latch);
1430 +            state = 2;
1431 +        }
1432 +    }
1433 +
1434 +    public LatchAwaiter awaiter(CountDownLatch latch) {
1435 +        return new LatchAwaiter(latch);
1436 +    }
1437 +
1438      public void await(CountDownLatch latch) {
1439          try {
1440 <            assertTrue(latch.await(LONG_DELAY_MS, MILLISECONDS));
1440 >            if (!latch.await(LONG_DELAY_MS, MILLISECONDS))
1441 >                fail("timed out waiting for CountDownLatch for "
1442 >                     + (LONG_DELAY_MS/1000) + " sec");
1443          } catch (Throwable fail) {
1444              threadUnexpectedException(fail);
1445          }
# Line 1253 | Line 1447 | public class JSR166TestCase extends Test
1447  
1448      public void await(Semaphore semaphore) {
1449          try {
1450 <            assertTrue(semaphore.tryAcquire(LONG_DELAY_MS, MILLISECONDS));
1450 >            if (!semaphore.tryAcquire(LONG_DELAY_MS, MILLISECONDS))
1451 >                fail("timed out waiting for Semaphore for "
1452 >                     + (LONG_DELAY_MS/1000) + " sec");
1453          } catch (Throwable fail) {
1454              threadUnexpectedException(fail);
1455          }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines