blob: 194dd58d0fafc632bfe1c349fb77acd48e166223 [file] [log] [blame]
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package jsr166;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.RunnableScheduledFuture;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import junit.framework.Test;
import junit.framework.TestSuite;
public class ScheduledExecutorSubclassTest extends JSR166TestCase {
// android-note: Removed because the CTS runner does a bad job of
// retrying tests that have suite() declarations.
//
// public static void main(String[] args) {
// main(suite(), args);
// }
// public static Test suite() {
// return new TestSuite(ScheduledExecutorSubclassTest.class);
// }
static class CustomTask<V> implements RunnableScheduledFuture<V> {
RunnableScheduledFuture<V> task;
volatile boolean ran;
CustomTask(RunnableScheduledFuture<V> t) { task = t; }
public boolean isPeriodic() { return task.isPeriodic(); }
public void run() {
ran = true;
task.run();
}
public long getDelay(TimeUnit unit) { return task.getDelay(unit); }
public int compareTo(Delayed t) {
return task.compareTo(((CustomTask)t).task);
}
public boolean cancel(boolean mayInterruptIfRunning) {
return task.cancel(mayInterruptIfRunning);
}
public boolean isCancelled() { return task.isCancelled(); }
public boolean isDone() { return task.isDone(); }
public V get() throws InterruptedException, ExecutionException {
V v = task.get();
assertTrue(ran);
return v;
}
public V get(long time, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
V v = task.get(time, unit);
assertTrue(ran);
return v;
}
}
public class CustomExecutor extends ScheduledThreadPoolExecutor {
protected <V> RunnableScheduledFuture<V> decorateTask(Runnable r, RunnableScheduledFuture<V> task) {
return new CustomTask<V>(task);
}
protected <V> RunnableScheduledFuture<V> decorateTask(Callable<V> c, RunnableScheduledFuture<V> task) {
return new CustomTask<V>(task);
}
CustomExecutor(int corePoolSize) { super(corePoolSize); }
CustomExecutor(int corePoolSize, RejectedExecutionHandler handler) {
super(corePoolSize, handler);
}
CustomExecutor(int corePoolSize, ThreadFactory threadFactory) {
super(corePoolSize, threadFactory);
}
CustomExecutor(int corePoolSize, ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize, threadFactory, handler);
}
}
/**
* execute successfully executes a runnable
*/
public void testExecute() throws InterruptedException {
final CustomExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p)) {
final CountDownLatch done = new CountDownLatch(1);
final Runnable task = new CheckedRunnable() {
public void realRun() { done.countDown(); }};
p.execute(task);
await(done);
}
}
/**
* delayed schedule of callable successfully executes after delay
*/
public void testSchedule1() throws Exception {
final CountDownLatch done = new CountDownLatch(1);
final CustomExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p, done)) {
final long startTime = System.nanoTime();
Callable task = new CheckedCallable<Boolean>() {
public Boolean realCall() {
done.countDown();
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
return Boolean.TRUE;
}};
Future f = p.schedule(task, timeoutMillis(), MILLISECONDS);
assertSame(Boolean.TRUE, f.get());
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
}
}
/**
* delayed schedule of runnable successfully executes after delay
*/
public void testSchedule3() throws Exception {
final CustomExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p)) {
final long startTime = System.nanoTime();
final CountDownLatch done = new CountDownLatch(1);
Runnable task = new CheckedRunnable() {
public void realRun() {
done.countDown();
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
}};
Future f = p.schedule(task, timeoutMillis(), MILLISECONDS);
await(done);
assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
}
}
/**
* scheduleAtFixedRate executes runnable after given initial delay
*/
public void testSchedule4() throws InterruptedException {
final CustomExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p)) {
final long startTime = System.nanoTime();
final CountDownLatch done = new CountDownLatch(1);
Runnable task = new CheckedRunnable() {
public void realRun() {
done.countDown();
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
}};
ScheduledFuture f =
p.scheduleAtFixedRate(task, timeoutMillis(),
LONG_DELAY_MS, MILLISECONDS);
await(done);
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
f.cancel(true);
}
}
/**
* scheduleWithFixedDelay executes runnable after given initial delay
*/
public void testSchedule5() throws InterruptedException {
final CustomExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p)) {
final long startTime = System.nanoTime();
final CountDownLatch done = new CountDownLatch(1);
Runnable task = new CheckedRunnable() {
public void realRun() {
done.countDown();
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
}};
ScheduledFuture f =
p.scheduleWithFixedDelay(task, timeoutMillis(),
LONG_DELAY_MS, MILLISECONDS);
await(done);
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
f.cancel(true);
}
}
static class RunnableCounter implements Runnable {
AtomicInteger count = new AtomicInteger(0);
public void run() { count.getAndIncrement(); }
}
/**
* scheduleAtFixedRate executes series of tasks at given rate.
* Eventually, it must hold that:
* cycles - 1 <= elapsedMillis/delay < cycles
*/
public void testFixedRateSequence() throws InterruptedException {
final CustomExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p)) {
for (int delay = 1; delay <= LONG_DELAY_MS; delay *= 3) {
final long startTime = System.nanoTime();
final int cycles = 8;
final CountDownLatch done = new CountDownLatch(cycles);
final Runnable task = new CheckedRunnable() {
public void realRun() { done.countDown(); }};
final ScheduledFuture periodicTask =
p.scheduleAtFixedRate(task, 0, delay, MILLISECONDS);
final int totalDelayMillis = (cycles - 1) * delay;
await(done, totalDelayMillis + LONG_DELAY_MS);
periodicTask.cancel(true);
final long elapsedMillis = millisElapsedSince(startTime);
assertTrue(elapsedMillis >= totalDelayMillis);
if (elapsedMillis <= cycles * delay)
return;
// else retry with longer delay
}
fail("unexpected execution rate");
}
}
/**
* scheduleWithFixedDelay executes series of tasks with given period.
* Eventually, it must hold that each task starts at least delay and at
* most 2 * delay after the termination of the previous task.
*/
public void testFixedDelaySequence() throws InterruptedException {
final CustomExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p)) {
for (int delay = 1; delay <= LONG_DELAY_MS; delay *= 3) {
final long startTime = System.nanoTime();
final AtomicLong previous = new AtomicLong(startTime);
final AtomicBoolean tryLongerDelay = new AtomicBoolean(false);
final int cycles = 8;
final CountDownLatch done = new CountDownLatch(cycles);
final int d = delay;
final Runnable task = new CheckedRunnable() {
public void realRun() {
long now = System.nanoTime();
long elapsedMillis
= NANOSECONDS.toMillis(now - previous.get());
if (done.getCount() == cycles) { // first execution
if (elapsedMillis >= d)
tryLongerDelay.set(true);
} else {
assertTrue(elapsedMillis >= d);
if (elapsedMillis >= 2 * d)
tryLongerDelay.set(true);
}
previous.set(now);
done.countDown();
}};
final ScheduledFuture periodicTask =
p.scheduleWithFixedDelay(task, 0, delay, MILLISECONDS);
final int totalDelayMillis = (cycles - 1) * delay;
await(done, totalDelayMillis + cycles * LONG_DELAY_MS);
periodicTask.cancel(true);
final long elapsedMillis = millisElapsedSince(startTime);
assertTrue(elapsedMillis >= totalDelayMillis);
if (!tryLongerDelay.get())
return;
// else retry with longer delay
}
fail("unexpected execution rate");
}
}
/**
* execute(null) throws NPE
*/
public void testExecuteNull() throws InterruptedException {
final CustomExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p)) {
try {
p.execute(null);
shouldThrow();
} catch (NullPointerException success) {}
}
}
/**
* schedule(null) throws NPE
*/
public void testScheduleNull() throws InterruptedException {
final CustomExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p)) {
try {
TrackedCallable callable = null;
Future f = p.schedule(callable, SHORT_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (NullPointerException success) {}
}
}
/**
* execute throws RejectedExecutionException if shutdown
*/
public void testSchedule1_RejectedExecutionException() {
final CustomExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p)) {
try {
p.shutdown();
p.schedule(new NoOpRunnable(),
MEDIUM_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (RejectedExecutionException success) {
} catch (SecurityException ok) {}
}
}
/**
* schedule throws RejectedExecutionException if shutdown
*/
public void testSchedule2_RejectedExecutionException() {
final CustomExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p)) {
try {
p.shutdown();
p.schedule(new NoOpCallable(),
MEDIUM_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (RejectedExecutionException success) {
} catch (SecurityException ok) {}
}
}
/**
* schedule callable throws RejectedExecutionException if shutdown
*/
public void testSchedule3_RejectedExecutionException() {
final CustomExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p)) {
try {
p.shutdown();
p.schedule(new NoOpCallable(),
MEDIUM_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (RejectedExecutionException success) {
} catch (SecurityException ok) {}
}
}
/**
* scheduleAtFixedRate throws RejectedExecutionException if shutdown
*/
public void testScheduleAtFixedRate1_RejectedExecutionException() {
final CustomExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p)) {
try {
p.shutdown();
p.scheduleAtFixedRate(new NoOpRunnable(),
MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (RejectedExecutionException success) {
} catch (SecurityException ok) {}
}
}
/**
* scheduleWithFixedDelay throws RejectedExecutionException if shutdown
*/
public void testScheduleWithFixedDelay1_RejectedExecutionException() {
final CustomExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p)) {
try {
p.shutdown();
p.scheduleWithFixedDelay(new NoOpRunnable(),
MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (RejectedExecutionException success) {
} catch (SecurityException ok) {}
}
}
/**
* getActiveCount increases but doesn't overestimate, when a
* thread becomes active
*/
public void testGetActiveCount() throws InterruptedException {
final CountDownLatch done = new CountDownLatch(1);
final ThreadPoolExecutor p = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(p, done)) {
final CountDownLatch threadStarted = new CountDownLatch(1);
assertEquals(0, p.getActiveCount());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
assertEquals(1, p.getActiveCount());
await(done);
}});
await(threadStarted);
assertEquals(1, p.getActiveCount());
}
}
/**
* getCompletedTaskCount increases, but doesn't overestimate,
* when tasks complete
*/
public void testGetCompletedTaskCount() throws InterruptedException {
final ThreadPoolExecutor p = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(p)) {
final CountDownLatch threadStarted = new CountDownLatch(1);
final CountDownLatch threadProceed = new CountDownLatch(1);
final CountDownLatch threadDone = new CountDownLatch(1);
assertEquals(0, p.getCompletedTaskCount());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
assertEquals(0, p.getCompletedTaskCount());
threadProceed.await();
threadDone.countDown();
}});
await(threadStarted);
assertEquals(0, p.getCompletedTaskCount());
threadProceed.countDown();
threadDone.await();
long startTime = System.nanoTime();
while (p.getCompletedTaskCount() != 1) {
if (millisElapsedSince(startTime) > LONG_DELAY_MS)
fail("timed out");
Thread.yield();
}
}
}
/**
* getCorePoolSize returns size given in constructor if not otherwise set
*/
public void testGetCorePoolSize() {
final CustomExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p)) {
assertEquals(1, p.getCorePoolSize());
}
}
/**
* getLargestPoolSize increases, but doesn't overestimate, when
* multiple threads active
*/
public void testGetLargestPoolSize() throws InterruptedException {
final int THREADS = 3;
final CountDownLatch done = new CountDownLatch(1);
final ThreadPoolExecutor p = new CustomExecutor(THREADS);
try (PoolCleaner cleaner = cleaner(p, done)) {
final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
assertEquals(0, p.getLargestPoolSize());
for (int i = 0; i < THREADS; i++)
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadsStarted.countDown();
await(done);
assertEquals(THREADS, p.getLargestPoolSize());
}});
await(threadsStarted);
assertEquals(THREADS, p.getLargestPoolSize());
}
assertEquals(THREADS, p.getLargestPoolSize());
}
/**
* getPoolSize increases, but doesn't overestimate, when threads
* become active
*/
public void testGetPoolSize() throws InterruptedException {
final CountDownLatch done = new CountDownLatch(1);
final ThreadPoolExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p, done)) {
final CountDownLatch threadStarted = new CountDownLatch(1);
assertEquals(0, p.getPoolSize());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
assertEquals(1, p.getPoolSize());
await(done);
}});
await(threadStarted);
assertEquals(1, p.getPoolSize());
}
}
/**
* getTaskCount increases, but doesn't overestimate, when tasks
* submitted
*/
public void testGetTaskCount() throws InterruptedException {
final int TASKS = 3;
final CountDownLatch done = new CountDownLatch(1);
final ThreadPoolExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p, done)) {
final CountDownLatch threadStarted = new CountDownLatch(1);
assertEquals(0, p.getTaskCount());
assertEquals(0, p.getCompletedTaskCount());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
await(done);
}});
await(threadStarted);
assertEquals(1, p.getTaskCount());
assertEquals(0, p.getCompletedTaskCount());
for (int i = 0; i < TASKS; i++) {
assertEquals(1 + i, p.getTaskCount());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
assertEquals(1 + TASKS, p.getTaskCount());
await(done);
}});
}
assertEquals(1 + TASKS, p.getTaskCount());
assertEquals(0, p.getCompletedTaskCount());
}
assertEquals(1 + TASKS, p.getTaskCount());
assertEquals(1 + TASKS, p.getCompletedTaskCount());
}
/**
* getThreadFactory returns factory in constructor if not set
*/
public void testGetThreadFactory() {
final ThreadFactory threadFactory = new SimpleThreadFactory();
final CustomExecutor p = new CustomExecutor(1, threadFactory);
try (PoolCleaner cleaner = cleaner(p)) {
assertSame(threadFactory, p.getThreadFactory());
}
}
/**
* setThreadFactory sets the thread factory returned by getThreadFactory
*/
public void testSetThreadFactory() {
final ThreadFactory threadFactory = new SimpleThreadFactory();
final CustomExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p)) {
p.setThreadFactory(threadFactory);
assertSame(threadFactory, p.getThreadFactory());
}
}
/**
* setThreadFactory(null) throws NPE
*/
public void testSetThreadFactoryNull() {
final CustomExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p)) {
try {
p.setThreadFactory(null);
shouldThrow();
} catch (NullPointerException success) {}
}
}
/**
* isShutdown is false before shutdown, true after
*/
public void testIsShutdown() {
final CustomExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p)) {
assertFalse(p.isShutdown());
try { p.shutdown(); } catch (SecurityException ok) { return; }
assertTrue(p.isShutdown());
}
}
/**
* isTerminated is false before termination, true after
*/
public void testIsTerminated() throws InterruptedException {
final CountDownLatch done = new CountDownLatch(1);
final ThreadPoolExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p)) {
final CountDownLatch threadStarted = new CountDownLatch(1);
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
assertFalse(p.isTerminated());
threadStarted.countDown();
await(done);
}});
await(threadStarted);
assertFalse(p.isTerminated());
assertFalse(p.isTerminating());
done.countDown();
try { p.shutdown(); } catch (SecurityException ok) { return; }
assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
assertTrue(p.isTerminated());
}
}
/**
* isTerminating is not true when running or when terminated
*/
public void testIsTerminating() throws InterruptedException {
final CountDownLatch done = new CountDownLatch(1);
final ThreadPoolExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p)) {
final CountDownLatch threadStarted = new CountDownLatch(1);
assertFalse(p.isTerminating());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
assertFalse(p.isTerminating());
threadStarted.countDown();
await(done);
}});
await(threadStarted);
assertFalse(p.isTerminating());
done.countDown();
try { p.shutdown(); } catch (SecurityException ok) { return; }
assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
assertTrue(p.isTerminated());
assertFalse(p.isTerminating());
}
}
/**
* getQueue returns the work queue, which contains queued tasks
*/
public void testGetQueue() throws InterruptedException {
final CountDownLatch done = new CountDownLatch(1);
final ScheduledThreadPoolExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p, done)) {
final CountDownLatch threadStarted = new CountDownLatch(1);
ScheduledFuture[] tasks = new ScheduledFuture[5];
for (int i = 0; i < tasks.length; i++) {
Runnable r = new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
await(done);
}};
tasks[i] = p.schedule(r, 1, MILLISECONDS);
}
await(threadStarted);
BlockingQueue<Runnable> q = p.getQueue();
assertTrue(q.contains(tasks[tasks.length - 1]));
assertFalse(q.contains(tasks[0]));
}
}
/**
* remove(task) removes queued task, and fails to remove active task
*/
public void testRemove() throws InterruptedException {
final CountDownLatch done = new CountDownLatch(1);
final ScheduledThreadPoolExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p, done)) {
ScheduledFuture[] tasks = new ScheduledFuture[5];
final CountDownLatch threadStarted = new CountDownLatch(1);
for (int i = 0; i < tasks.length; i++) {
Runnable r = new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
await(done);
}};
tasks[i] = p.schedule(r, 1, MILLISECONDS);
}
await(threadStarted);
BlockingQueue<Runnable> q = p.getQueue();
assertFalse(p.remove((Runnable)tasks[0]));
assertTrue(q.contains((Runnable)tasks[4]));
assertTrue(q.contains((Runnable)tasks[3]));
assertTrue(p.remove((Runnable)tasks[4]));
assertFalse(p.remove((Runnable)tasks[4]));
assertFalse(q.contains((Runnable)tasks[4]));
assertTrue(q.contains((Runnable)tasks[3]));
assertTrue(p.remove((Runnable)tasks[3]));
assertFalse(q.contains((Runnable)tasks[3]));
}
}
/**
* purge removes cancelled tasks from the queue
*/
public void testPurge() throws InterruptedException {
final ScheduledFuture[] tasks = new ScheduledFuture[5];
final Runnable releaser = new Runnable() { public void run() {
for (ScheduledFuture task : tasks)
if (task != null) task.cancel(true); }};
final CustomExecutor p = new CustomExecutor(1);
try (PoolCleaner cleaner = cleaner(p, releaser)) {
for (int i = 0; i < tasks.length; i++)
tasks[i] = p.schedule(new SmallPossiblyInterruptedRunnable(),
LONG_DELAY_MS, MILLISECONDS);
int max = tasks.length;
if (tasks[4].cancel(true)) --max;
if (tasks[3].cancel(true)) --max;
// There must eventually be an interference-free point at
// which purge will not fail. (At worst, when queue is empty.)
long startTime = System.nanoTime();
do {
p.purge();
long count = p.getTaskCount();
if (count == max)
return;
} while (millisElapsedSince(startTime) < LONG_DELAY_MS);
fail("Purge failed to remove cancelled tasks");
}
}
/**
* shutdownNow returns a list containing tasks that were not run,
* and those tasks are drained from the queue
*/
public void testShutdownNow() throws InterruptedException {
final int poolSize = 2;
final int count = 5;
final AtomicInteger ran = new AtomicInteger(0);
final CustomExecutor p = new CustomExecutor(poolSize);
final CountDownLatch threadsStarted = new CountDownLatch(poolSize);
Runnable waiter = new CheckedRunnable() { public void realRun() {
threadsStarted.countDown();
try {
MILLISECONDS.sleep(2 * LONG_DELAY_MS);
} catch (InterruptedException success) {}
ran.getAndIncrement();
}};
for (int i = 0; i < count; i++)
p.execute(waiter);
await(threadsStarted);
assertEquals(poolSize, p.getActiveCount());
assertEquals(0, p.getCompletedTaskCount());
final List<Runnable> queuedTasks;
try {
queuedTasks = p.shutdownNow();
} catch (SecurityException ok) {
return; // Allowed in case test doesn't have privs
}
assertTrue(p.isShutdown());
assertTrue(p.getQueue().isEmpty());
assertEquals(count - poolSize, queuedTasks.size());
assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
assertTrue(p.isTerminated());
assertEquals(poolSize, ran.get());
assertEquals(poolSize, p.getCompletedTaskCount());
}
/**
* shutdownNow returns a list containing tasks that were not run,
* and those tasks are drained from the queue
*/
public void testShutdownNow_delayedTasks() throws InterruptedException {
final CustomExecutor p = new CustomExecutor(1);
List<ScheduledFuture> tasks = new ArrayList<>();
for (int i = 0; i < 3; i++) {
Runnable r = new NoOpRunnable();
tasks.add(p.schedule(r, 9, SECONDS));
tasks.add(p.scheduleAtFixedRate(r, 9, 9, SECONDS));
tasks.add(p.scheduleWithFixedDelay(r, 9, 9, SECONDS));
}
if (testImplementationDetails)
assertEquals(new HashSet(tasks), new HashSet(p.getQueue()));
final List<Runnable> queuedTasks;
try {
queuedTasks = p.shutdownNow();
} catch (SecurityException ok) {
return; // Allowed in case test doesn't have privs
}
assertTrue(p.isShutdown());
assertTrue(p.getQueue().isEmpty());
if (testImplementationDetails)
assertEquals(new HashSet(tasks), new HashSet(queuedTasks));
assertEquals(tasks.size(), queuedTasks.size());
for (ScheduledFuture task : tasks) {
assertFalse(((CustomTask)task).ran);
assertFalse(task.isDone());
assertFalse(task.isCancelled());
}
assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
assertTrue(p.isTerminated());
}
/**
* By default, periodic tasks are cancelled at shutdown.
* By default, delayed tasks keep running after shutdown.
* Check that changing the default values work:
* - setExecuteExistingDelayedTasksAfterShutdownPolicy
* - setContinueExistingPeriodicTasksAfterShutdownPolicy
*/
public void testShutdown_cancellation() throws Exception {
Boolean[] allBooleans = { null, Boolean.FALSE, Boolean.TRUE };
for (Boolean policy : allBooleans)
{
final int poolSize = 2;
final CustomExecutor p = new CustomExecutor(poolSize);
final boolean effectiveDelayedPolicy = (policy != Boolean.FALSE);
final boolean effectivePeriodicPolicy = (policy == Boolean.TRUE);
final boolean effectiveRemovePolicy = (policy == Boolean.TRUE);
if (policy != null) {
p.setExecuteExistingDelayedTasksAfterShutdownPolicy(policy);
p.setContinueExistingPeriodicTasksAfterShutdownPolicy(policy);
p.setRemoveOnCancelPolicy(policy);
}
assertEquals(effectiveDelayedPolicy,
p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
assertEquals(effectivePeriodicPolicy,
p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
assertEquals(effectiveRemovePolicy,
p.getRemoveOnCancelPolicy());
// Strategy: Wedge the pool with poolSize "blocker" threads
final AtomicInteger ran = new AtomicInteger(0);
final CountDownLatch poolBlocked = new CountDownLatch(poolSize);
final CountDownLatch unblock = new CountDownLatch(1);
final CountDownLatch periodicLatch1 = new CountDownLatch(2);
final CountDownLatch periodicLatch2 = new CountDownLatch(2);
Runnable task = new CheckedRunnable() { public void realRun()
throws InterruptedException {
poolBlocked.countDown();
assertTrue(unblock.await(LONG_DELAY_MS, MILLISECONDS));
ran.getAndIncrement();
}};
List<Future<?>> blockers = new ArrayList<>();
List<Future<?>> periodics = new ArrayList<>();
List<Future<?>> delayeds = new ArrayList<>();
for (int i = 0; i < poolSize; i++)
blockers.add(p.submit(task));
assertTrue(poolBlocked.await(LONG_DELAY_MS, MILLISECONDS));
periodics.add(p.scheduleAtFixedRate(countDowner(periodicLatch1),
1, 1, MILLISECONDS));
periodics.add(p.scheduleWithFixedDelay(countDowner(periodicLatch2),
1, 1, MILLISECONDS));
delayeds.add(p.schedule(task, 1, MILLISECONDS));
assertTrue(p.getQueue().containsAll(periodics));
assertTrue(p.getQueue().containsAll(delayeds));
try { p.shutdown(); } catch (SecurityException ok) { return; }
assertTrue(p.isShutdown());
assertFalse(p.isTerminated());
for (Future<?> periodic : periodics) {
assertTrue(effectivePeriodicPolicy ^ periodic.isCancelled());
assertTrue(effectivePeriodicPolicy ^ periodic.isDone());
}
for (Future<?> delayed : delayeds) {
assertTrue(effectiveDelayedPolicy ^ delayed.isCancelled());
assertTrue(effectiveDelayedPolicy ^ delayed.isDone());
}
if (testImplementationDetails) {
assertEquals(effectivePeriodicPolicy,
p.getQueue().containsAll(periodics));
assertEquals(effectiveDelayedPolicy,
p.getQueue().containsAll(delayeds));
}
// Release all pool threads
unblock.countDown();
for (Future<?> delayed : delayeds) {
if (effectiveDelayedPolicy) {
assertNull(delayed.get());
}
}
if (effectivePeriodicPolicy) {
assertTrue(periodicLatch1.await(LONG_DELAY_MS, MILLISECONDS));
assertTrue(periodicLatch2.await(LONG_DELAY_MS, MILLISECONDS));
for (Future<?> periodic : periodics) {
assertTrue(periodic.cancel(false));
assertTrue(periodic.isCancelled());
assertTrue(periodic.isDone());
}
}
assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
assertTrue(p.isTerminated());
assertEquals(2 + (effectiveDelayedPolicy ? 1 : 0), ran.get());
}}
/**
* completed submit of callable returns result
*/
public void testSubmitCallable() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
Future<String> future = e.submit(new StringTask());
String result = future.get();
assertSame(TEST_STRING, result);
}
}
/**
* completed submit of runnable returns successfully
*/
public void testSubmitRunnable() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
Future<?> future = e.submit(new NoOpRunnable());
future.get();
assertTrue(future.isDone());
}
}
/**
* completed submit of (runnable, result) returns result
*/
public void testSubmitRunnable2() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
String result = future.get();
assertSame(TEST_STRING, result);
}
}
/**
* invokeAny(null) throws NPE
*/
public void testInvokeAny1() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
try {
e.invokeAny(null);
shouldThrow();
} catch (NullPointerException success) {}
}
}
/**
* invokeAny(empty collection) throws IAE
*/
public void testInvokeAny2() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
try {
e.invokeAny(new ArrayList<Callable<String>>());
shouldThrow();
} catch (IllegalArgumentException success) {}
}
}
/**
* invokeAny(c) throws NPE if c has null elements
*/
public void testInvokeAny3() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(latchAwaitingStringTask(latch));
l.add(null);
try {
e.invokeAny(l);
shouldThrow();
} catch (NullPointerException success) {}
latch.countDown();
}
}
/**
* invokeAny(c) throws ExecutionException if no task completes
*/
public void testInvokeAny4() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new NPETask());
try {
e.invokeAny(l);
shouldThrow();
} catch (ExecutionException success) {
assertTrue(success.getCause() instanceof NullPointerException);
}
}
}
/**
* invokeAny(c) returns result of some task
*/
public void testInvokeAny5() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
String result = e.invokeAny(l);
assertSame(TEST_STRING, result);
}
}
/**
* invokeAll(null) throws NPE
*/
public void testInvokeAll1() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
try {
e.invokeAll(null);
shouldThrow();
} catch (NullPointerException success) {}
}
}
/**
* invokeAll(empty collection) returns empty collection
*/
public void testInvokeAll2() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
assertTrue(r.isEmpty());
}
}
/**
* invokeAll(c) throws NPE if c has null elements
*/
public void testInvokeAll3() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(null);
try {
e.invokeAll(l);
shouldThrow();
} catch (NullPointerException success) {}
}
}
/**
* get of invokeAll(c) throws exception on failed task
*/
public void testInvokeAll4() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new NPETask());
List<Future<String>> futures = e.invokeAll(l);
assertEquals(1, futures.size());
try {
futures.get(0).get();
shouldThrow();
} catch (ExecutionException success) {
assertTrue(success.getCause() instanceof NullPointerException);
}
}
}
/**
* invokeAll(c) returns results of all completed tasks
*/
public void testInvokeAll5() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
List<Future<String>> futures = e.invokeAll(l);
assertEquals(2, futures.size());
for (Future<String> future : futures)
assertSame(TEST_STRING, future.get());
}
}
/**
* timed invokeAny(null) throws NPE
*/
public void testTimedInvokeAny1() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
try {
e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (NullPointerException success) {}
}
}
/**
* timed invokeAny(,,null) throws NPE
*/
public void testTimedInvokeAnyNullTimeUnit() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
try {
e.invokeAny(l, MEDIUM_DELAY_MS, null);
shouldThrow();
} catch (NullPointerException success) {}
}
}
/**
* timed invokeAny(empty collection) throws IAE
*/
public void testTimedInvokeAny2() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
try {
e.invokeAny(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (IllegalArgumentException success) {}
}
}
/**
* timed invokeAny(c) throws NPE if c has null elements
*/
public void testTimedInvokeAny3() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(latchAwaitingStringTask(latch));
l.add(null);
try {
e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (NullPointerException success) {}
latch.countDown();
}
}
/**
* timed invokeAny(c) throws ExecutionException if no task completes
*/
public void testTimedInvokeAny4() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
long startTime = System.nanoTime();
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new NPETask());
try {
e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (ExecutionException success) {
assertTrue(success.getCause() instanceof NullPointerException);
}
assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}
}
/**
* timed invokeAny(c) returns result of some task
*/
public void testTimedInvokeAny5() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
long startTime = System.nanoTime();
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
assertSame(TEST_STRING, result);
assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}
}
/**
* timed invokeAll(null) throws NPE
*/
public void testTimedInvokeAll1() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
try {
e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (NullPointerException success) {}
}
}
/**
* timed invokeAll(,,null) throws NPE
*/
public void testTimedInvokeAllNullTimeUnit() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
try {
e.invokeAll(l, MEDIUM_DELAY_MS, null);
shouldThrow();
} catch (NullPointerException success) {}
}
}
/**
* timed invokeAll(empty collection) returns empty collection
*/
public void testTimedInvokeAll2() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
assertTrue(r.isEmpty());
}
}
/**
* timed invokeAll(c) throws NPE if c has null elements
*/
public void testTimedInvokeAll3() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(null);
try {
e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (NullPointerException success) {}
}
}
/**
* get of element of invokeAll(c) throws exception on failed task
*/
public void testTimedInvokeAll4() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new NPETask());
List<Future<String>> futures =
e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
assertEquals(1, futures.size());
try {
futures.get(0).get();
shouldThrow();
} catch (ExecutionException success) {
assertTrue(success.getCause() instanceof NullPointerException);
}
}
}
/**
* timed invokeAll(c) returns results of all completed tasks
*/
public void testTimedInvokeAll5() throws Exception {
final ExecutorService e = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
List<Future<String>> futures =
e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
assertEquals(2, futures.size());
for (Future<String> future : futures)
assertSame(TEST_STRING, future.get());
}
}
/**
* timed invokeAll(c) cancels tasks not completed by timeout
*/
public void testTimedInvokeAll6() throws Exception {
for (long timeout = timeoutMillis();;) {
final CountDownLatch done = new CountDownLatch(1);
final Callable<String> waiter = new CheckedCallable<String>() {
public String realCall() {
try { done.await(LONG_DELAY_MS, MILLISECONDS); }
catch (InterruptedException ok) {}
return "1"; }};
final ExecutorService p = new CustomExecutor(2);
try (PoolCleaner cleaner = cleaner(p, done)) {
List<Callable<String>> tasks = new ArrayList<>();
tasks.add(new StringTask("0"));
tasks.add(waiter);
tasks.add(new StringTask("2"));
long startTime = System.nanoTime();
List<Future<String>> futures =
p.invokeAll(tasks, timeout, MILLISECONDS);
assertEquals(tasks.size(), futures.size());
assertTrue(millisElapsedSince(startTime) >= timeout);
for (Future future : futures)
assertTrue(future.isDone());
assertTrue(futures.get(1).isCancelled());
try {
assertEquals("0", futures.get(0).get());
assertEquals("2", futures.get(2).get());
break;
} catch (CancellationException retryWithLongerTimeout) {
timeout *= 2;
if (timeout >= LONG_DELAY_MS / 2)
fail("expected exactly one task to be cancelled");
}
}
}
}
}