jdk/test/java/util/concurrent/tck/JSR166TestCase.java
changeset 45936 d564ef4bed8f
parent 44589 64d9270bd24c
child 46146 b3e220a04d3f
--- a/jdk/test/java/util/concurrent/tck/JSR166TestCase.java	Sat Jul 22 09:08:50 2017 -0700
+++ b/jdk/test/java/util/concurrent/tck/JSR166TestCase.java	Sat Jul 22 09:13:53 2017 -0700
@@ -103,18 +103,24 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.CyclicBarrier;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ForkJoinPool;
 import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
 import java.util.concurrent.RecursiveAction;
 import java.util.concurrent.RecursiveTask;
+import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.RejectedExecutionHandler;
 import java.util.concurrent.Semaphore;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
@@ -665,6 +671,33 @@
     public static long MEDIUM_DELAY_MS;
     public static long LONG_DELAY_MS;
 
+    private static final long RANDOM_TIMEOUT;
+    private static final long RANDOM_EXPIRED_TIMEOUT;
+    private static final TimeUnit RANDOM_TIMEUNIT;
+    static {
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        long[] timeouts = { Long.MIN_VALUE, -1, 0, 1, Long.MAX_VALUE };
+        RANDOM_TIMEOUT = timeouts[rnd.nextInt(timeouts.length)];
+        RANDOM_EXPIRED_TIMEOUT = timeouts[rnd.nextInt(3)];
+        TimeUnit[] timeUnits = TimeUnit.values();
+        RANDOM_TIMEUNIT = timeUnits[rnd.nextInt(timeUnits.length)];
+    }
+
+    /**
+     * Returns a timeout for use when any value at all will do.
+     */
+    static long randomTimeout() { return RANDOM_TIMEOUT; }
+
+    /**
+     * Returns a timeout that means "no waiting", i.e. not positive.
+     */
+    static long randomExpiredTimeout() { return RANDOM_EXPIRED_TIMEOUT; }
+
+    /**
+     * Returns a random non-null TimeUnit.
+     */
+    static TimeUnit randomTimeUnit() { return RANDOM_TIMEUNIT; }
+
     /**
      * Returns the shortest timed delay. This can be scaled up for
      * slow machines using the jsr166.delay.factor system property,
@@ -685,12 +718,17 @@
         LONG_DELAY_MS   = SHORT_DELAY_MS * 200;
     }
 
+    private static final long TIMEOUT_DELAY_MS
+        = (long) (12.0 * Math.cbrt(delayFactor));
+
     /**
-     * Returns a timeout in milliseconds to be used in tests that
-     * verify that operations block or time out.
+     * Returns a timeout in milliseconds to be used in tests that verify
+     * that operations block or time out.  We want this to be longer
+     * than the OS scheduling quantum, but not too long, so don't scale
+     * linearly with delayFactor; we use "crazy" cube root instead.
      */
-    long timeoutMillis() {
-        return SHORT_DELAY_MS / 4;
+    static long timeoutMillis() {
+        return TIMEOUT_DELAY_MS;
     }
 
     /**
@@ -1085,8 +1123,29 @@
     }
 
     /**
+     * Checks that thread eventually enters the expected blocked thread state.
+     */
+    void assertThreadBlocks(Thread thread, Thread.State expected) {
+        // always sleep at least 1 ms, with high probability avoiding
+        // transitory states
+        for (long retries = LONG_DELAY_MS * 3 / 4; retries-->0; ) {
+            try { delay(1); }
+            catch (InterruptedException fail) {
+                fail("Unexpected InterruptedException");
+            }
+            Thread.State s = thread.getState();
+            if (s == expected)
+                return;
+            else if (s == Thread.State.TERMINATED)
+                fail("Unexpected thread termination");
+        }
+        fail("timed out waiting for thread to enter thread state " + expected);
+    }
+
+    /**
      * Checks that thread does not terminate within the default
      * millisecond delay of {@code timeoutMillis()}.
+     * TODO: REMOVEME
      */
     void assertThreadStaysAlive(Thread thread) {
         assertThreadStaysAlive(thread, timeoutMillis());
@@ -1094,6 +1153,7 @@
 
     /**
      * Checks that thread does not terminate within the given millisecond delay.
+     * TODO: REMOVEME
      */
     void assertThreadStaysAlive(Thread thread, long millis) {
         try {
@@ -1108,6 +1168,7 @@
     /**
      * Checks that the threads do not terminate within the default
      * millisecond delay of {@code timeoutMillis()}.
+     * TODO: REMOVEME
      */
     void assertThreadsStayAlive(Thread... threads) {
         assertThreadsStayAlive(timeoutMillis(), threads);
@@ -1115,6 +1176,7 @@
 
     /**
      * Checks that the threads do not terminate within the given millisecond delay.
+     * TODO: REMOVEME
      */
     void assertThreadsStayAlive(long millis, Thread... threads) {
         try {
@@ -1165,6 +1227,12 @@
     }
 
     /**
+     * The maximum number of consecutive spurious wakeups we should
+     * tolerate (from APIs like LockSupport.park) before failing a test.
+     */
+    static final int MAX_SPURIOUS_WAKEUPS = 10;
+
+    /**
      * The number of elements to place in collections, arrays, etc.
      */
     public static final int SIZE = 20;
@@ -1633,6 +1701,14 @@
         }
     }
 
+    public void await(CyclicBarrier barrier) {
+        try {
+            barrier.await(LONG_DELAY_MS, MILLISECONDS);
+        } catch (Throwable fail) {
+            threadUnexpectedException(fail);
+        }
+    }
+
 //     /**
 //      * Spin-waits up to LONG_DELAY_MS until flag becomes true.
 //      */
@@ -1656,28 +1732,6 @@
         public String call() { throw new NullPointerException(); }
     }
 
-    public static class CallableOne implements Callable<Integer> {
-        public Integer call() { return one; }
-    }
-
-    public class ShortRunnable extends CheckedRunnable {
-        protected void realRun() throws Throwable {
-            delay(SHORT_DELAY_MS);
-        }
-    }
-
-    public class ShortInterruptedRunnable extends CheckedInterruptedRunnable {
-        protected void realRun() throws InterruptedException {
-            delay(SHORT_DELAY_MS);
-        }
-    }
-
-    public class SmallRunnable extends CheckedRunnable {
-        protected void realRun() throws Throwable {
-            delay(SMALL_DELAY_MS);
-        }
-    }
-
     public class SmallPossiblyInterruptedRunnable extends CheckedRunnable {
         protected void realRun() {
             try {
@@ -1686,25 +1740,6 @@
         }
     }
 
-    public class SmallCallable extends CheckedCallable {
-        protected Object realCall() throws InterruptedException {
-            delay(SMALL_DELAY_MS);
-            return Boolean.TRUE;
-        }
-    }
-
-    public class MediumRunnable extends CheckedRunnable {
-        protected void realRun() throws Throwable {
-            delay(MEDIUM_DELAY_MS);
-        }
-    }
-
-    public class MediumInterruptedRunnable extends CheckedInterruptedRunnable {
-        protected void realRun() throws InterruptedException {
-            delay(MEDIUM_DELAY_MS);
-        }
-    }
-
     public Runnable possiblyInterruptedRunnable(final long timeoutMillis) {
         return new CheckedRunnable() {
             protected void realRun() {
@@ -1714,22 +1749,6 @@
             }};
     }
 
-    public class MediumPossiblyInterruptedRunnable extends CheckedRunnable {
-        protected void realRun() {
-            try {
-                delay(MEDIUM_DELAY_MS);
-            } catch (InterruptedException ok) {}
-        }
-    }
-
-    public class LongPossiblyInterruptedRunnable extends CheckedRunnable {
-        protected void realRun() {
-            try {
-                delay(LONG_DELAY_MS);
-            } catch (InterruptedException ok) {}
-        }
-    }
-
     /**
      * For use as ThreadFactory in constructors
      */
@@ -1743,59 +1762,6 @@
         boolean isDone();
     }
 
-    public static TrackedRunnable trackedRunnable(final long timeoutMillis) {
-        return new TrackedRunnable() {
-                private volatile boolean done = false;
-                public boolean isDone() { return done; }
-                public void run() {
-                    try {
-                        delay(timeoutMillis);
-                        done = true;
-                    } catch (InterruptedException ok) {}
-                }
-            };
-    }
-
-    public static class TrackedShortRunnable implements Runnable {
-        public volatile boolean done = false;
-        public void run() {
-            try {
-                delay(SHORT_DELAY_MS);
-                done = true;
-            } catch (InterruptedException ok) {}
-        }
-    }
-
-    public static class TrackedSmallRunnable implements Runnable {
-        public volatile boolean done = false;
-        public void run() {
-            try {
-                delay(SMALL_DELAY_MS);
-                done = true;
-            } catch (InterruptedException ok) {}
-        }
-    }
-
-    public static class TrackedMediumRunnable implements Runnable {
-        public volatile boolean done = false;
-        public void run() {
-            try {
-                delay(MEDIUM_DELAY_MS);
-                done = true;
-            } catch (InterruptedException ok) {}
-        }
-    }
-
-    public static class TrackedLongRunnable implements Runnable {
-        public volatile boolean done = false;
-        public void run() {
-            try {
-                delay(LONG_DELAY_MS);
-                done = true;
-            } catch (InterruptedException ok) {}
-        }
-    }
-
     public static class TrackedNoOpRunnable implements Runnable {
         public volatile boolean done = false;
         public void run() {
@@ -1803,17 +1769,6 @@
         }
     }
 
-    public static class TrackedCallable implements Callable {
-        public volatile boolean done = false;
-        public Object call() {
-            try {
-                delay(SMALL_DELAY_MS);
-                done = true;
-            } catch (InterruptedException ok) {}
-            return Boolean.TRUE;
-        }
-    }
-
     /**
      * Analog of CheckedRunnable for RecursiveAction
      */
@@ -1880,7 +1835,7 @@
             assertEquals(0, q.size());
             assertNull(q.peek());
             assertNull(q.poll());
-            assertNull(q.poll(0, MILLISECONDS));
+            assertNull(q.poll(randomExpiredTimeout(), randomTimeUnit()));
             assertEquals(q.toString(), "[]");
             assertTrue(Arrays.equals(q.toArray(), new Object[0]));
             assertFalse(q.iterator().hasNext());
@@ -2031,4 +1986,176 @@
     static <T> void shuffle(T[] array) {
         Collections.shuffle(Arrays.asList(array), ThreadLocalRandom.current());
     }
+
+    // --- Shared assertions for Executor tests ---
+
+    /**
+     * Returns maximum number of tasks that can be submitted to given
+     * pool (with bounded queue) before saturation (when submission
+     * throws RejectedExecutionException).
+     */
+    static final int saturatedSize(ThreadPoolExecutor pool) {
+        BlockingQueue<Runnable> q = pool.getQueue();
+        return pool.getMaximumPoolSize() + q.size() + q.remainingCapacity();
+    }
+
+    @SuppressWarnings("FutureReturnValueIgnored")
+    void assertNullTaskSubmissionThrowsNullPointerException(Executor e) {
+        try {
+            e.execute((Runnable) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+
+        if (! (e instanceof ExecutorService)) return;
+        ExecutorService es = (ExecutorService) e;
+        try {
+            es.submit((Runnable) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+        try {
+            es.submit((Runnable) null, Boolean.TRUE);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+        try {
+            es.submit((Callable) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+
+        if (! (e instanceof ScheduledExecutorService)) return;
+        ScheduledExecutorService ses = (ScheduledExecutorService) e;
+        try {
+            ses.schedule((Runnable) null,
+                         randomTimeout(), randomTimeUnit());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+        try {
+            ses.schedule((Callable) null,
+                         randomTimeout(), randomTimeUnit());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+        try {
+            ses.scheduleAtFixedRate((Runnable) null,
+                                    randomTimeout(), LONG_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+        try {
+            ses.scheduleWithFixedDelay((Runnable) null,
+                                       randomTimeout(), LONG_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    void setRejectedExecutionHandler(
+        ThreadPoolExecutor p, RejectedExecutionHandler handler) {
+        p.setRejectedExecutionHandler(handler);
+        assertSame(handler, p.getRejectedExecutionHandler());
+    }
+
+    void assertTaskSubmissionsAreRejected(ThreadPoolExecutor p) {
+        final RejectedExecutionHandler savedHandler = p.getRejectedExecutionHandler();
+        final long savedTaskCount = p.getTaskCount();
+        final long savedCompletedTaskCount = p.getCompletedTaskCount();
+        final int savedQueueSize = p.getQueue().size();
+        final boolean stock = (p.getClass().getClassLoader() == null);
+
+        Runnable r = () -> {};
+        Callable<Boolean> c = () -> Boolean.TRUE;
+
+        class Recorder implements RejectedExecutionHandler {
+            public volatile Runnable r = null;
+            public volatile ThreadPoolExecutor p = null;
+            public void reset() { r = null; p = null; }
+            public void rejectedExecution(Runnable r, ThreadPoolExecutor p) {
+                assertNull(this.r);
+                assertNull(this.p);
+                this.r = r;
+                this.p = p;
+            }
+        }
+
+        // check custom handler is invoked exactly once per task
+        Recorder recorder = new Recorder();
+        setRejectedExecutionHandler(p, recorder);
+        for (int i = 2; i--> 0; ) {
+            recorder.reset();
+            p.execute(r);
+            if (stock && p.getClass() == ThreadPoolExecutor.class)
+                assertSame(r, recorder.r);
+            assertSame(p, recorder.p);
+
+            recorder.reset();
+            assertFalse(p.submit(r).isDone());
+            if (stock) assertTrue(!((FutureTask) recorder.r).isDone());
+            assertSame(p, recorder.p);
+
+            recorder.reset();
+            assertFalse(p.submit(r, Boolean.TRUE).isDone());
+            if (stock) assertTrue(!((FutureTask) recorder.r).isDone());
+            assertSame(p, recorder.p);
+
+            recorder.reset();
+            assertFalse(p.submit(c).isDone());
+            if (stock) assertTrue(!((FutureTask) recorder.r).isDone());
+            assertSame(p, recorder.p);
+
+            if (p instanceof ScheduledExecutorService) {
+                ScheduledExecutorService s = (ScheduledExecutorService) p;
+                ScheduledFuture<?> future;
+
+                recorder.reset();
+                future = s.schedule(r, randomTimeout(), randomTimeUnit());
+                assertFalse(future.isDone());
+                if (stock) assertTrue(!((FutureTask) recorder.r).isDone());
+                assertSame(p, recorder.p);
+
+                recorder.reset();
+                future = s.schedule(c, randomTimeout(), randomTimeUnit());
+                assertFalse(future.isDone());
+                if (stock) assertTrue(!((FutureTask) recorder.r).isDone());
+                assertSame(p, recorder.p);
+
+                recorder.reset();
+                future = s.scheduleAtFixedRate(r, randomTimeout(), LONG_DELAY_MS, MILLISECONDS);
+                assertFalse(future.isDone());
+                if (stock) assertTrue(!((FutureTask) recorder.r).isDone());
+                assertSame(p, recorder.p);
+
+                recorder.reset();
+                future = s.scheduleWithFixedDelay(r, randomTimeout(), LONG_DELAY_MS, MILLISECONDS);
+                assertFalse(future.isDone());
+                if (stock) assertTrue(!((FutureTask) recorder.r).isDone());
+                assertSame(p, recorder.p);
+            }
+        }
+
+        // Checking our custom handler above should be sufficient, but
+        // we add some integration tests of standard handlers.
+        final AtomicReference<Thread> thread = new AtomicReference<>();
+        final Runnable setThread = () -> thread.set(Thread.currentThread());
+
+        setRejectedExecutionHandler(p, new ThreadPoolExecutor.AbortPolicy());
+        try {
+            p.execute(setThread);
+            shouldThrow();
+        } catch (RejectedExecutionException success) {}
+        assertNull(thread.get());
+
+        setRejectedExecutionHandler(p, new ThreadPoolExecutor.DiscardPolicy());
+        p.execute(setThread);
+        assertNull(thread.get());
+
+        setRejectedExecutionHandler(p, new ThreadPoolExecutor.CallerRunsPolicy());
+        p.execute(setThread);
+        if (p.isShutdown())
+            assertNull(thread.get());
+        else
+            assertSame(Thread.currentThread(), thread.get());
+
+        setRejectedExecutionHandler(p, savedHandler);
+
+        // check that pool was not perturbed by handlers
+        assertEquals(savedTaskCount, p.getTaskCount());
+        assertEquals(savedCompletedTaskCount, p.getCompletedTaskCount());
+        assertEquals(savedQueueSize, p.getQueue().size());
+    }
 }