--- 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());
+ }
}