/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.concurrent;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.*;
import java.util.concurrent.atomic.*;
import java.util.*;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.utils.Cachetable;
import org.apache.cassandra.utils.GuidGenerator;
import org.apache.cassandra.utils.ICachetable;
import org.apache.cassandra.utils.LogUtil;
import org.apache.commons.javaflow.Continuation;
import org.apache.log4j.Logger;
/**
* An {@link ExecutorService} that executes each submitted task using one of
* possibly several pooled threads, normally configured using {@link Executors}
* factory methods.
*
* <p>
* Thread pools address two different problems: they usually provide improved
* performance when executing large numbers of asynchronous tasks, due to
* reduced per-task invocation overhead, and they provide a means of bounding
* and managing the resources, including threads, consumed when executing a
* collection of tasks. Each {@code ContinuationsExecutor} also maintains some
* basic statistics, such as the number of completed tasks.
*
* <p>
* To be useful across a wide range of contexts, this class provides many
* adjustable parameters and extensibility hooks. However, programmers are urged
* to use the more convenient {@link Executors} factory methods {@link
* Executors#newCachedThreadPool} (unbounded thread pool, with automatic thread
* reclamation), {@link Executors#newFixedThreadPool} (fixed size thread pool)
* and {@link Executors#newSingleThreadExecutor} (single background thread),
* that preconfigure settings for the most common usage scenarios. Otherwise,
* use the following guide when manually configuring and tuning this class:
*
* <dl>
*
* <dt>Core and maximum pool sizes</dt>
*
* <dd>A {@code ContinuationsExecutor} will automatically adjust the pool size
* (see {@link #getPoolSize}) according to the bounds set by corePoolSize (see
* {@link #getCorePoolSize}) and maximumPoolSize (see
* {@link #getMaximumPoolSize}).
*
* When a new task is submitted in method {@link #execute}, and fewer than
* corePoolSize threads are running, a new thread is created to handle the
* request, even if other worker threads are idle. If there are more than
* corePoolSize but less than maximumPoolSize threads running, a new thread will
* be created only if the queue is full. By setting corePoolSize and
* maximumPoolSize the same, you create a fixed-size thread pool. By setting
* maximumPoolSize to an essentially unbounded value such as
* {@code Integer.MAX_VALUE}, you allow the pool to accommodate an arbitrary
* number of concurrent tasks. Most typically, core and maximum pool sizes are
* set only upon construction, but they may also be changed dynamically using
* {@link #setCorePoolSize} and {@link #setMaximumPoolSize}. </dd>
*
* <dt>On-demand construction</dt>
*
* <dd> By default, even core threads are initially created and started only
* when new tasks arrive, but this can be overridden dynamically using method
* {@link #prestartCoreThread} or {@link #prestartAllCoreThreads}. You probably
* want to prestart threads if you construct the pool with a non-empty queue.
* </dd>
*
* <dt>Creating new threads</dt>
*
* <dd>New threads are created using a {@link ThreadFactory}. If not otherwise
* specified, a {@link Executors#defaultThreadFactory} is used, that creates
* threads to all be in the same {@link ThreadGroup} and with the same
* {@code NORM_PRIORITY} priority and non-daemon status. By supplying a
* different ThreadFactory, you can alter the thread's name, thread group,
* priority, daemon status, etc. If a {@code ThreadFactory} fails to create a
* thread when asked by returning null from {@code newThread}, the executor
* will continue, but might not be able to execute any tasks. Threads should
* possess the "modifyThread" {@code RuntimePermission}. If worker threads or
* other threads using the pool do not possess this permission, service may be
* degraded: configuration changes may not take effect in a timely manner, and a
* shutdown pool may remain in a state in which termination is possible but not
* completed.</dd>
*
* <dt>Keep-alive times</dt>
*
* <dd>If the pool currently has more than corePoolSize threads, excess threads
* will be terminated if they have been idle for more than the keepAliveTime
* (see {@link #getKeepAliveTime}). This provides a means of reducing resource
* consumption when the pool is not being actively used. If the pool becomes
* more active later, new threads will be constructed. This parameter can also
* be changed dynamically using method {@link #setKeepAliveTime}. Using a value
* of {@code Long.MAX_VALUE} {@link TimeUnit#NANOSECONDS} effectively disables
* idle threads from ever terminating prior to shut down. By default, the
* keep-alive policy applies only when there are more than corePoolSizeThreads.
* But method {@link #allowCoreThreadTimeOut(boolean)} can be used to apply this
* time-out policy to core threads as well, so long as the keepAliveTime value
* is non-zero. </dd>
*
* <dt>Queuing</dt>
*
* <dd>Any {@link BlockingQueue} may be used to transfer and hold submitted
* tasks. The use of this queue interacts with pool sizing:
*
* <ul>
*
* <li> If fewer than corePoolSize threads are running, the Executor always
* prefers adding a new thread rather than queuing.</li>
*
* <li> If corePoolSize or more threads are running, the Executor always prefers
* queuing a request rather than adding a new thread.</li>
*
* <li> If a request cannot be queued, a new thread is created unless this would
* exceed maximumPoolSize, in which case, the task will be rejected.</li>
*
* </ul>
*
* There are three general strategies for queuing:
* <ol>
*
* <li> <em> Direct handoffs.</em> A good default choice for a work queue is a
* {@link SynchronousQueue} that hands off tasks to threads without otherwise
* holding them. Here, an attempt to queue a task will fail if no threads are
* immediately available to run it, so a new thread will be constructed. This
* policy avoids lockups when handling sets of requests that might have internal
* dependencies. Direct handoffs generally require unbounded maximumPoolSizes to
* avoid rejection of new submitted tasks. This in turn admits the possibility
* of unbounded thread growth when commands continue to arrive on average faster
* than they can be processed. </li>
*
* <li><em> Unbounded queues.</em> Using an unbounded queue (for example a
* {@link LinkedBlockingQueue} without a predefined capacity) will cause new
* tasks to wait in the queue when all corePoolSize threads are busy. Thus, no
* more than corePoolSize threads will ever be created. (And the value of the
* maximumPoolSize therefore doesn't have any effect.) This may be appropriate
* when each task is completely independent of others, so tasks cannot affect
* each others execution; for example, in a web page server. While this style of
* queuing can be useful in smoothing out transient bursts of requests, it
* admits the possibility of unbounded work queue growth when commands continue
* to arrive on average faster than they can be processed. </li>
*
* <li><em>Bounded queues.</em> A bounded queue (for example, an
* {@link ArrayBlockingQueue}) helps prevent resource exhaustion when used with
* finite maximumPoolSizes, but can be more difficult to tune and control. Queue
* sizes and maximum pool sizes may be traded off for each other: Using large
* queues and small pools minimizes CPU usage, OS resources, and
* context-switching overhead, but can lead to artificially low throughput. If
* tasks frequently block (for example if they are I/O bound), a system may be
* able to schedule time for more threads than you otherwise allow. Use of small
* queues generally requires larger pool sizes, which keeps CPUs busier but may
* encounter unacceptable scheduling overhead, which also decreases throughput.
* </li>
*
* </ol>
*
* </dd>
*
* <dt>Rejected tasks</dt>
*
* <dd> New tasks submitted in method {@link #execute} will be <em>rejected</em>
* when the Executor has been shut down, and also when the Executor uses finite
* bounds for both maximum threads and work queue capacity, and is saturated. In
* either case, the {@code execute} method invokes the {@link
* RejectedExecutionHandler#rejectedExecution} method of its {@link
* RejectedExecutionHandler}. Four predefined handler policies are provided:
*
* <ol>
*
* <li> In the default {@link ContinuationsExecutor.AbortPolicy}, the handler
* throws a runtime {@link RejectedExecutionException} upon rejection. </li>
*
* <li> In {@link ContinuationsExecutor.CallerRunsPolicy}, the thread that invokes
* {@code execute} itself runs the task. This provides a simple feedback control
* mechanism that will slow down the rate that new tasks are submitted. </li>
*
* <li> In {@link ContinuationsExecutor.DiscardPolicy}, a task that cannot be
* executed is simply dropped. </li>
*
* <li>In {@link ContinuationsExecutor.DiscardOldestPolicy}, if the executor is
* not shut down, the task at the head of the work queue is dropped, and then
* execution is retried (which can fail again, causing this to be repeated.)
* </li>
*
* </ol>
*
* It is possible to define and use other kinds of {@link
* RejectedExecutionHandler} classes. Doing so requires some care especially
* when policies are designed to work only under particular capacity or queuing
* policies. </dd>
*
* <dt>Hook methods</dt>
*
* <dd>This class provides {@code protected} overridable {@link #beforeExecute}
* and {@link #afterExecute} methods that are called before and after execution
* of each task. These can be used to manipulate the execution environment; for
* example, reinitializing ThreadLocals, gathering statistics, or adding log
* entries. Additionally, method {@link #terminated} can be overridden to
* perform any special processing that needs to be done once the Executor has
* fully terminated.
*
* <p>
* If hook or callback methods throw exceptions, internal worker threads may in
* turn fail and abruptly terminate.</dd>
*
* <dt>Queue maintenance</dt>
*
* <dd> Method {@link #getQueue} allows access to the work queue for purposes of
* monitoring and debugging. Use of this method for any other purpose is
* strongly discouraged. Two supplied methods, {@link #remove} and
* {@link #purge} are available to assist in storage reclamation when large
* numbers of queued tasks become cancelled.</dd>
*
* <dt>Finalization</dt>
*
* <dd> A pool that is no longer referenced in a program <em>AND</em> has no
* remaining threads will be {@code shutdown} automatically. If you would like
* to ensure that unreferenced pools are reclaimed even if users forget to call
* {@link #shutdown}, then you must arrange that unused threads eventually die,
* by setting appropriate keep-alive times, using a lower bound of zero core
* threads and/or setting {@link #allowCoreThreadTimeOut(boolean)}. </dd>
*
* </dl>
*
* <p>
* <b>Extension example</b>. Most extensions of this class override one or more
* of the protected hook methods. For example, here is a subclass that adds a
* simple pause/resume feature:
*
* <pre>
* {@code
* class PausableThreadPoolExecutor extends ContinuationsExecutor {
* private boolean isPaused;
* private ReentrantLock pauseLock = new ReentrantLock();
* private Condition unpaused = pauseLock.newCondition();
*
* public PausableThreadPoolExecutor(...) { super(...); }
*
* protected void beforeExecute(Thread t, Runnable r) {
* super.beforeExecute(t, r);
* pauseLock.lock();
* try {
* while (isPaused) unpaused.await();
* } catch (InterruptedException ie) {
* t.interrupt();
* } finally {
* pauseLock.unlock();
* }
* }
*
* public void pause() {
* pauseLock.lock();
* try {
* isPaused = true;
* } finally {
* pauseLock.unlock();
* }
* }
*
* public void resume() {
* pauseLock.lock();
* try {
* isPaused = false;
* unpaused.signalAll();
* } finally {
* pauseLock.unlock();
* }
* }
* }}
* </pre>
*
* @since 1.5
* @author Doug Lea
*/
public class ContinuationsExecutor extends AbstractExecutorService
{
/**
* The main pool control state, ctl, is an atomic integer packing two
* conceptual fields workerCount, indicating the effective number of threads
* runState, indicating whether running, shutting down etc
*
* In order to pack them into one int, we limit workerCount to (2^29)-1
* (about 500 million) threads rather than (2^31)-1 (2 billion) otherwise
* representable. If this is ever an issue in the future, the variable can
* be changed to be an AtomicLong, and the shift/mask constants below
* adjusted. But until the need arises, this code is a bit faster and
* simpler using an int.
*
* The workerCount is the number of workers that have been permitted to
* start and not permitted to stop. The value may be transiently different
* from the actual number of live threads, for example when a ThreadFactory
* fails to create a thread when asked, and when exiting threads are still
* performing bookkeeping before terminating. The user-visible pool size is
* reported as the current size of the workers set.
*
* The runState provides the main lifecyle control, taking on values:
*
* RUNNING: Accept new tasks and process queued tasks SHUTDOWN: Don't accept
* new tasks, but process queued tasks STOP: Don't accept new tasks, don't
* process queued tasks, and interrupt in-progress tasks TIDYING: All tasks
* have terminated, workerCount is zero, the thread transitioning to state
* TIDYING will run the terminated() hook method TERMINATED: terminated()
* has completed
*
* The numerical order among these values matters, to allow ordered
* comparisons. The runState monotonically increases over time, but need not
* hit each state. The transitions are:
*
* RUNNING -> SHUTDOWN On invocation of shutdown(), perhaps implicitly in
* finalize() (RUNNING or SHUTDOWN) -> STOP On invocation of shutdownNow()
* SHUTDOWN -> TIDYING When both queue and pool are empty STOP -> TIDYING
* When pool is empty TIDYING -> TERMINATED When the terminated() hook
* method has completed
*
* Threads waiting in awaitTermination() will return when the state reaches
* TERMINATED.
*
* Detecting the transition from SHUTDOWN to TIDYING is less straightforward
* than you'd like because the queue may become empty after non-empty and
* vice versa during SHUTDOWN state, but we can only terminate if, after
* seeing that it is empty, we see that workerCount is 0 (which sometimes
* entails a recheck -- see below).
*/
private final static Logger logger_ = Logger.getLogger( ContinuationsExecutor.class );
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private final static ThreadLocal<IContinuable> tls_ = new ThreadLocal<IContinuable>();
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
public static void putInTls(IContinuable run)
{
tls_.set(run);
}
public static void doPostProcessing(Continuation c)
{
/* post process the call if need be */
IContinuable run = ContinuationsExecutor.tls_.get();
if ( run != null )
{
run.run(c);
}
ContinuationsExecutor.tls_.remove();
}
// Packing and unpacking ctl
private static int runStateOf(int c)
{
return c & ~CAPACITY;
}
private static int workerCountOf(int c)
{
return c & CAPACITY;
}
private static int ctlOf(int rs, int wc)
{
return rs | wc;
}
/*
* Bit field accessors that don't require unpacking ctl. These depend on the
* bit layout and on workerCount being never negative.
*/
private static boolean runStateLessThan(int c, int s)
{
return c < s;
}
private static boolean runStateAtLeast(int c, int s)
{
return c >= s;
}
private static boolean isRunning(int c)
{
return c < SHUTDOWN;
}
/**
* Attempt to CAS-increment the workerCount field of ctl.
*/
private boolean compareAndIncrementWorkerCount(int expect)
{
return ctl.compareAndSet(expect, expect + 1);
}
/**
* Attempt to CAS-decrement the workerCount field of ctl.
*/
private boolean compareAndDecrementWorkerCount(int expect)
{
return ctl.compareAndSet(expect, expect - 1);
}
/**
* Decrements the workerCount field of ctl. This is called only on abrupt
* termination of a thread (see processWorkerExit). Other decrements are
* performed within getTask.
*/
private void decrementWorkerCount()
{
do
{
}
while (!compareAndDecrementWorkerCount(ctl.get()));
}
/**
* The queue used for holding tasks and handing off to worker threads. We do
* not require that workQueue.poll() returning null necessarily means that
* workQueue.isEmpty(), so rely solely on isEmpty to see if the queue is
* empty (which we must do for example when deciding whether to transition
* from SHUTDOWN to TIDYING). This accommodates special-purpose queues such
* as DelayQueues for which poll() is allowed to return null even if it may
* later return non-null when delays expire.
*/
private final BlockingQueue<Runnable> workQueue;
/**
* Lock held on access to workers set and related bookkeeping. While we
* could use a concurrent set of some sort, it turns out to be generally
* preferable to use a lock. Among the reasons is that this serializes
* interruptIdleWorkers, which avoids unnecessary interrupt storms,
* especially during shutdown. Otherwise exiting threads would concurrently
* interrupt those that have not yet interrupted. It also simplifies some of
* the associated statistics bookkeeping of largestPoolSize etc. We also
* hold mainLock on shutdown and shutdownNow, for the sake of ensuring
* workers set is stable while separately checking permission to interrupt
* and actually interrupting.
*/
private final ReentrantLock mainLock = new ReentrantLock();
/**
* Set containing all worker threads in pool. Accessed only when holding
* mainLock.
*/
private final HashSet<Worker> workers = new HashSet<Worker>();
/**
* Wait condition to support awaitTermination
*/
private final Condition termination = mainLock.newCondition();
/**
* Tracks largest attained pool size. Accessed only under mainLock.
*/
private int largestPoolSize;
/**
* Counter for completed tasks. Updated only on termination of worker
* threads. Accessed only under mainLock.
*/
private long completedTaskCount;
/*
* All user control parameters are declared as volatiles so that ongoing
* actions are based on freshest values, but without need for locking, since
* no internal invariants depend on them changing synchronously with respect
* to other actions.
*/
/**
* Factory for new threads. All threads are created using this factory (via
* method addWorker). All callers must be prepared for addWorker to fail,
* which may reflect a system or user's policy limiting the number of
* threads. Even though it is not treated as an error, failure to create
* threads may result in new tasks being rejected or existing ones remaining
* stuck in the queue. On the other hand, no special precautions exist to
* handle OutOfMemoryErrors that might be thrown while trying to create
* threads, since there is generally no recourse from within this class.
*/
private volatile ThreadFactory threadFactory;
/**
* Handler called when saturated or shutdown in execute.
*/
private volatile RejectedExecutionHandler handler;
/**
* Timeout in nanoseconds for idle threads waiting for work. Threads use
* this timeout when there are more than corePoolSize present or if
* allowCoreThreadTimeOut. Otherwise they wait forever for new work.
*/
private volatile long keepAliveTime;
/**
* If false (default), core threads stay alive even when idle. If true, core
* threads use keepAliveTime to time out waiting for work.
*/
private volatile boolean allowCoreThreadTimeOut;
/**
* Core pool size is the minimum number of workers to keep alive (and not
* allow to time out etc) unless allowCoreThreadTimeOut is set, in which
* case the minimum is zero.
*/
private volatile int corePoolSize;
/**
* Maximum pool size. Note that the actual maximum is internally bounded by
* CAPACITY.
*/
private volatile int maximumPoolSize;
/**
* The default rejected execution handler
*/
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
/**
* Permission required for callers of shutdown and shutdownNow. We
* additionally require (see checkShutdownAccess) that callers have
* permission to actually interrupt threads in the worker set (as governed
* by Thread.interrupt, which relies on ThreadGroup.checkAccess, which in
* turn relies on SecurityManager.checkAccess). Shutdowns are attempted only
* if these checks pass.
*
* All actual invocations of Thread.interrupt (see interruptIdleWorkers and
* interruptWorkers) ignore SecurityExceptions, meaning that the attempted
* interrupts silently fail. In the case of shutdown, they should not fail
* unless the SecurityManager has inconsistent policies, sometimes allowing
* access to a thread and sometimes not. In such cases, failure to actually
* interrupt threads may disable or delay full termination. Other uses of
* interruptIdleWorkers are advisory, and failure to actually interrupt will
* merely delay response to configuration changes so is not handled
* exceptionally.
*/
private static final RuntimePermission shutdownPerm = new RuntimePermission(
"modifyThread");
/**
* Class Worker mainly maintains interrupt control state for threads running
* tasks, along with other minor bookkeeping. This class opportunistically
* extends AbstractQueuedSynchronizer to simplify acquiring and releasing a
* lock surrounding each task execution. This protects against interrupts
* that are intended to wake up a worker thread waiting for a task from
* instead interrupting a task being run. We implement a simple
* non-reentrant mutual exclusion lock rather than use ReentrantLock because
* we do not want worker tasks to be able to reacquire the lock when they
* invoke pool control methods like setCorePoolSize.
*/
private final class Worker extends AbstractQueuedSynchronizer implements Runnable
{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
/** Thread this worker is running in. Null if factory fails. */
final Thread thread;
/** Initial task to run. Possibly null. */
Runnable firstTask;
/** Per-thread task counter */
volatile long completedTasks;
/**
* Creates with given first task and thread from ThreadFactory.
*
* @param firstTask
* the first task (null if none)
*/
Worker(Runnable firstTask)
{
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker */
public void run()
{
runWorker(this);
}
// Lock methods
//
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
protected boolean isHeldExclusively()
{
return getState() == 1;
}
protected boolean tryAcquire(int unused)
{
if (compareAndSetState(0, 1))
{
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int unused)
{
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock()
{
acquire(1);
}
public boolean tryLock()
{
return tryAcquire(1);
}
public void unlock()
{
release(1);
}
public boolean isLocked()
{
return isHeldExclusively();
}
}
/*
* Methods for setting control state
*/
/**
* Transitions runState to given target, or leaves it alone if already at
* least the given target.
*
* @param targetState
* the desired state, either SHUTDOWN or STOP (but not TIDYING or
* TERMINATED -- use tryTerminate for that)
*/
private void advanceRunState(int targetState)
{
for (;;)
{
int c = ctl.get();
if (runStateAtLeast(c, targetState)
|| ctl.compareAndSet(c,
ctlOf(targetState, workerCountOf(c))))
break;
}
}
/**
* Transitions to TERMINATED state if either (SHUTDOWN and pool and queue
* empty) or (STOP and pool empty). If otherwise eligible to terminate but
* workerCount is nonzero, interrupts an idle worker to ensure that shutdown
* signals propagate. This method must be called following any action that
* might make termination possible -- reducing worker count or removing
* tasks from the queue during shutdown. The method is non-private to allow
* access from ScheduledThreadPoolExecutor.
*/
final void tryTerminate()
{
for (;;)
{
int c = ctl.get();
if (isRunning(c) || runStateAtLeast(c, TIDYING)
|| (runStateOf(c) == SHUTDOWN && !workQueue.isEmpty()))
return;
if (workerCountOf(c) != 0)
{ // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try
{
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0)))
{
try
{
terminated();
}
finally
{
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
}
finally
{
mainLock.unlock();
}
// else retry on failed CAS
}
}
/*
* Methods for controlling interrupts to worker threads.
*/
/**
* If there is a security manager, makes sure caller has permission to shut
* down threads in general (see shutdownPerm). If this passes, additionally
* makes sure the caller is allowed to interrupt each worker thread. This
* might not be true even if first check passed, if the SecurityManager
* treats some threads specially.
*/
private void checkShutdownAccess()
{
SecurityManager security = System.getSecurityManager();
if (security != null)
{
security.checkPermission(shutdownPerm);
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try
{
for (Worker w : workers)
security.checkAccess(w.thread);
}
finally
{
mainLock.unlock();
}
}
}
/**
* Interrupts all threads, even if active. Ignores SecurityExceptions (in
* which case some threads may remain uninterrupted).
*/
private void interruptWorkers()
{
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try
{
for (Worker w : workers)
{
try
{
w.thread.interrupt();
}
catch (SecurityException ignore)
{
}
}
}
finally
{
mainLock.unlock();
}
}
/**
* Interrupts threads that might be waiting for tasks (as indicated by not
* being locked) so they can check for termination or configuration changes.
* Ignores SecurityExceptions (in which case some threads may remain
* uninterrupted).
*
* @param onlyOne
* If true, interrupt at most one worker. This is called only
* from tryTerminate when termination is otherwise enabled but
* there are still other workers. In this case, at most one
* waiting worker is interrupted to propagate shutdown signals in
* case all threads are currently waiting. Interrupting any
* arbitrary thread ensures that newly arriving workers since
* shutdown began will also eventually exit. To guarantee
* eventual termination, it suffices to always interrupt only one
* idle worker, but shutdown() interrupts all idle workers so
* that redundant workers exit promptly, not waiting for a
* straggler task to finish.
*/
private void interruptIdleWorkers(boolean onlyOne)
{
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try
{
for (Worker w : workers)
{
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock())
{
try
{
t.interrupt();
}
catch (SecurityException ignore)
{
}
finally
{
w.unlock();
}
}
if (onlyOne)
break;
}
}
finally
{
mainLock.unlock();
}
}
/**
* Common form of interruptIdleWorkers, to avoid having to remember what the
* boolean argument means.
*/
private void interruptIdleWorkers()
{
interruptIdleWorkers(false);
}
private static final boolean ONLY_ONE = true;
/**
* Ensures that unless the pool is stopping, the current thread does not
* have its interrupt set. This requires a double-check of state in case the
* interrupt was cleared concurrently with a shutdownNow -- if so, the
* interrupt is re-enabled.
*/
private void clearInterruptsForTaskRun()
{
if (runStateLessThan(ctl.get(), STOP) && Thread.interrupted()
&& runStateAtLeast(ctl.get(), STOP))
Thread.currentThread().interrupt();
}
/*
* Misc utilities, most of which are also exported to
* ScheduledThreadPoolExecutor
*/
/**
* Invokes the rejected execution handler for the given command.
* Package-protected for use by ScheduledThreadPoolExecutor.
*/
final void reject(Runnable command)
{
handler.rejectedExecution(command, this);
}
/**
* Performs any further cleanup following run state transition on invocation
* of shutdown. A no-op here, but used by ScheduledThreadPoolExecutor to
* cancel delayed tasks.
*/
void onShutdown()
{
}
/**
* State check needed by ScheduledThreadPoolExecutor to enable running tasks
* during shutdown.
*
* @param shutdownOK
* true if should return true if SHUTDOWN
*/
final boolean isRunningOrShutdown(boolean shutdownOK)
{
int rs = runStateOf(ctl.get());
return rs == RUNNING || (rs == SHUTDOWN && shutdownOK);
}
/**
* Drains the task queue into a new list, normally using drainTo. But if the
* queue is a DelayQueue or any other kind of queue for which poll or
* drainTo may fail to remove some elements, it deletes them one by one.
*/
private List<Runnable> drainQueue()
{
BlockingQueue<Runnable> q = workQueue;
List<Runnable> taskList = new ArrayList<Runnable>();
q.drainTo(taskList);
if (!q.isEmpty())
{
for (Runnable r : q.toArray(new Runnable[0]))
{
if (q.remove(r))
taskList.add(r);
}
}
return taskList;
}
/*
* Methods for creating, running and cleaning up after workers
*/
/**
* Checks if a new worker can be added with respect to current pool state
* and the given bound (either core or maximum). If so, the worker count is
* adjusted accordingly, and, if possible, a new worker is created and
* started running firstTask as its first task. This method returns false if
* the pool is stopped or eligible to shut down. It also returns false if
* the thread factory fails to create a thread when asked, which requires a
* backout of workerCount, and a recheck for termination, in case the
* existence of this worker was holding up termination.
*
* @param firstTask
* the task the new thread should run first (or null if none).
* Workers are created with an initial first task (in method
* execute()) to bypass queuing when there are fewer than
* corePoolSize threads (in which case we always start one), or
* when the queue is full (in which case we must bypass queue).
* Initially idle threads are usually created via
* prestartCoreThread or to replace other dying workers.
*
* @param core
* if true use corePoolSize as bound, else maximumPoolSize. (A
* boolean indicator is used here rather than a value to ensure
* reads of fresh values after checking other pool state).
* @return true if successful
*/
private boolean addWorker(Runnable firstTask, boolean core)
{
retry: for (;;)
{
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN
&& !(rs == SHUTDOWN && firstTask == null && !workQueue
.isEmpty()))
return false;
for (;;)
{
int wc = workerCountOf(c);
if (wc >= CAPACITY
|| wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
Worker w = new Worker(firstTask);
Thread t = w.thread;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try
{
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int c = ctl.get();
int rs = runStateOf(c);
if (t == null
|| (rs >= SHUTDOWN && !(rs == SHUTDOWN && firstTask == null)))
{
decrementWorkerCount();
tryTerminate();
return false;
}
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
}
finally
{
mainLock.unlock();
}
t.start();
// It is possible (but unlikely) for a thread to have been
// added to workers, but not yet started, during transition to
// STOP, which could result in a rare missed interrupt,
// because Thread.interrupt is not guaranteed to have any effect
// on a non-yet-started Thread (see Thread#interrupt).
if (runStateOf(ctl.get()) == STOP && !t.isInterrupted())
t.interrupt();
return true;
}
/**
* Performs cleanup and bookkeeping for a dying worker. Called only from
* worker threads. Unless completedAbruptly is set, assumes that workerCount
* has already been adjusted to account for exit. This method removes thread
* from worker set, and possibly terminates the pool or replaces the worker
* if either it exited due to user task exception or if fewer than
* corePoolSize workers are running or queue is non-empty but there are no
* workers.
*
* @param w
* the worker
* @param completedAbruptly
* if the worker died due to user exception
*/
private void processWorkerExit(Worker w, boolean completedAbruptly)
{
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try
{
completedTaskCount += w.completedTasks;
workers.remove(w);
}
finally
{
mainLock.unlock();
}
tryTerminate();
int c = ctl.get();
if (runStateLessThan(c, STOP))
{
if (!completedAbruptly)
{
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && !workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}
/**
* Performs blocking or timed wait for a task, depending on current
* configuration settings, or returns null if this worker must exit because
* of any of: 1. There are more than maximumPoolSize workers (due to a call
* to setMaximumPoolSize). 2. The pool is stopped. 3. The pool is shutdown
* and the queue is empty. 4. This worker timed out waiting for a task, and
* timed-out workers are subject to termination (that is,
* {@code allowCoreThreadTimeOut || workerCount > corePoolSize}) both
* before and after the timed wait.
*
* @return task, or null if the worker must exit, in which case workerCount
* is decremented
*/
private Runnable getTask()
{
boolean timedOut = false; // Did the last poll() time out?
retry: for (;;)
{
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty()))
{
decrementWorkerCount();
return null;
}
boolean timed; // Are workers subject to culling?
for (;;)
{
int wc = workerCountOf(c);
timed = allowCoreThreadTimeOut || wc > corePoolSize;
if (wc <= maximumPoolSize && !(timedOut && timed))
break;
if (compareAndDecrementWorkerCount(c))
return null;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
try
{
Runnable r = timed ? workQueue.poll(keepAliveTime,
TimeUnit.NANOSECONDS) : workQueue.take();
if (r != null)
return r;
timedOut = true;
}
catch (InterruptedException retry)
{
timedOut = false;
}
}
}
/**
* Main worker run loop. Repeatedly gets tasks from queue and executes them,
* while coping with a number of issues:
*
* 1. We may start out with an initial task, in which case we don't need to
* get the first one. Otherwise, as long as pool is running, we get tasks
* from getTask. If it returns null then the worker exits due to changed
* pool state or configuration parameters. Other exits result from exception
* throws in external code, in which case completedAbruptly holds, which
* usually leads processWorkerExit to replace this thread.
*
* 2. Before running any task, the lock is acquired to prevent other pool
* interrupts while the task is executing, and clearInterruptsForTaskRun
* called to ensure that unless pool is stopping, this thread does not have
* its interrupt set.
*
* 3. Each task run is preceded by a call to beforeExecute, which might
* throw an exception, in which case we cause thread to die (breaking loop
* with completedAbruptly true) without processing the task.
*
* 4. Assuming beforeExecute completes normally, we run the task, gathering
* any of its thrown exceptions to send to afterExecute. We separately
* handle RuntimeException, Error (both of which the specs guarantee that we
* trap) and arbitrary Throwables. Because we cannot rethrow Throwables
* within Runnable.run, we wrap them within Errors on the way out (to the
* thread's UncaughtExceptionHandler). Any thrown exception also
* conservatively causes thread to die.
*
* 5. After task.run completes, we call afterExecute, which may also throw
* an exception, which will also cause thread to die. According to JLS Sec
* 14.20, this exception is the one that will be in effect even if task.run
* throws.
*
* The net effect of the exception mechanics is that afterExecute and the
* thread's UncaughtExceptionHandler have as accurate information as we can
* provide about any problems encountered by user code.
*
* @param w
* the worker
*/
final void runWorker(Worker w)
{
Runnable task = w.firstTask;
w.firstTask = null;
boolean completedAbruptly = true;
try
{
while (task != null || (task = getTask()) != null)
{
w.lock();
clearInterruptsForTaskRun();
try
{
beforeExecute(w.thread, task);
Throwable thrown = null;
try
{
/* start in suspended mode to get a handle to the continuation */
Continuation c = Continuation.startSuspendedWith(task);
/* resume the damn continuation */
c = Continuation.continueWith(c, new ContinuationContext(c));
/* post process the call if need be */
ContinuationsExecutor.doPostProcessing(c);
}
catch (RuntimeException x)
{
thrown = x;
throw x;
}
catch (Error x)
{
thrown = x;
throw x;
}
catch (Throwable x)
{
thrown = x;
throw new Error(x);
}
finally
{
afterExecute(task, thrown);
}
}
finally
{
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
}
finally
{
processWorkerExit(w, completedAbruptly);
}
}
// Public constructors and methods
/**
* Creates a new {@code ContinuationsExecutor} with the given initial
* parameters and default thread factory and rejected execution handler. It
* may be more convenient to use one of the {@link Executors} factory
* methods instead of this general purpose constructor.
*
* @param corePoolSize
* the number of threads to keep in the pool, even if they are
* idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize
* the maximum number of threads to allow in the pool
* @param keepAliveTime
* when the number of threads is greater than the core, this is
* the maximum time that excess idle threads will wait for new
* tasks before terminating.
* @param unit
* the time unit for the {@code keepAliveTime} argument
* @param workQueue
* the queue to use for holding tasks before they are executed.
* This queue will hold only the {@code Runnable} tasks submitted
* by the {@code execute} method.
* @throws IllegalArgumentException
* if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException
* if {@code workQueue} is null
*/
public ContinuationsExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
{
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
/**
* Creates a new {@code ContinuationsExecutor} with the given initial
* parameters and default rejected execution handler.
*
* @param corePoolSize
* the number of threads to keep in the pool, even if they are
* idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize
* the maximum number of threads to allow in the pool
* @param keepAliveTime
* when the number of threads is greater than the core, this is
* the maximum time that excess idle threads will wait for new
* tasks before terminating.
* @param unit
* the time unit for the {@code keepAliveTime} argument
* @param workQueue
* the queue to use for holding tasks before they are executed.
* This queue will hold only the {@code Runnable} tasks submitted
* by the {@code execute} method.
* @param threadFactory
* the factory to use when the executor creates a new thread
* @throws IllegalArgumentException
* if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException
* if {@code workQueue} or {@code threadFactory} is null
*/
public ContinuationsExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
{
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
/**
* Creates a new {@code ContinuationsExecutor} with the given initial
* parameters and default thread factory.
*
* @param corePoolSize
* the number of threads to keep in the pool, even if they are
* idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize
* the maximum number of threads to allow in the pool
* @param keepAliveTime
* when the number of threads is greater than the core, this is
* the maximum time that excess idle threads will wait for new
* tasks before terminating.
* @param unit
* the time unit for the {@code keepAliveTime} argument
* @param workQueue
* the queue to use for holding tasks before they are executed.
* This queue will hold only the {@code Runnable} tasks submitted
* by the {@code execute} method.
* @param handler
* the handler to use when execution is blocked because the
* thread bounds and queue capacities are reached
* @throws IllegalArgumentException
* if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException
* if {@code workQueue} or {@code handler} is null
*/
public ContinuationsExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
{
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
/**
* Creates a new {@code ContinuationsExecutor} with the given initial
* parameters.
*
* @param corePoolSize
* the number of threads to keep in the pool, even if they are
* idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize
* the maximum number of threads to allow in the pool
* @param keepAliveTime
* when the number of threads is greater than the core, this is
* the maximum time that excess idle threads will wait for new
* tasks before terminating.
* @param unit
* the time unit for the {@code keepAliveTime} argument
* @param workQueue
* the queue to use for holding tasks before they are executed.
* This queue will hold only the {@code Runnable} tasks submitted
* by the {@code execute} method.
* @param threadFactory
* the factory to use when the executor creates a new thread
* @param handler
* the handler to use when execution is blocked because the
* thread bounds and queue capacities are reached
* @throws IllegalArgumentException
* if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException
* if {@code workQueue} or {@code threadFactory} or
* {@code handler} is null
*/
public ContinuationsExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)
{
if (corePoolSize < 0 || maximumPoolSize <= 0
|| maximumPoolSize < corePoolSize || keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
/**
* Executes the given task sometime in the future. The task may execute in a
* new thread or in an existing pooled thread.
*
* If the task cannot be submitted for execution, either because this
* executor has been shutdown or because its capacity has been reached, the
* task is handled by the current {@code RejectedExecutionHandler}.
*
* @param command
* the task to execute
* @throws RejectedExecutionException
* at discretion of {@code RejectedExecutionHandler}, if the
* task cannot be accepted for execution
* @throws NullPointerException
* if {@code command} is null
*/
public void execute(Runnable command)
{
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to start a new
* thread with the given command as its first task. The call to
* addWorker atomically checks runState and workerCount, and so prevents
* false alarms that would add threads when it shouldn't, by returning
* false.
*
* 2. If a task can be successfully queued, then we still need to
* double-check whether we should have added a thread (because existing
* ones died since last checking) or that the pool shut down since entry
* into this method. So we recheck state and if necessary roll back the
* enqueuing if stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new thread. If it
* fails, we know we are shut down or saturated and so reject the task.
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize)
{
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command))
{
int recheck = ctl.get();
if (!isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
/**
* Initiates an orderly shutdown in which previously submitted tasks are
* executed, but no new tasks will be accepted. Invocation has no additional
* effect if already shut down.
*
* @throws SecurityException
* {@inheritDoc}
*/
public void shutdown()
{
/*
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try
{
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
}
finally
{
mainLock.unlock();
}
tryTerminate();
*/
}
/**
* Attempts to stop all actively executing tasks, halts the processing of
* waiting tasks, and returns a list of the tasks that were awaiting
* execution. These tasks are drained (removed) from the task queue upon
* return from this method.
*
* <p>
* There are no guarantees beyond best-effort attempts to stop processing
* actively executing tasks. This implementation cancels tasks via
* {@link Thread#interrupt}, so any task that fails to respond to
* interrupts may never terminate.
*
* @throws SecurityException
* {@inheritDoc}
*/
public List<Runnable> shutdownNow()
{
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try
{
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
}
finally
{
mainLock.unlock();
}
tryTerminate();
return tasks;
}
public boolean isShutdown()
{
return !isRunning(ctl.get());
}
/**
* Returns true if this executor is in the process of terminating after
* {@link #shutdown} or {@link #shutdownNow} but has not completely
* terminated. This method may be useful for debugging. A return of
* {@code true} reported a sufficient period after shutdown may indicate
* that submitted tasks have ignored or suppressed interruption, causing
* this executor not to properly terminate.
*
* @return true if terminating but not yet terminated
*/
public boolean isTerminating()
{
int c = ctl.get();
return !isRunning(c) && runStateLessThan(c, TERMINATED);
}
public boolean isTerminated()
{
return runStateAtLeast(ctl.get(), TERMINATED);
}
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException
{
long nanos = unit.toNanos(timeout);
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try
{
for (;;)
{
if (runStateAtLeast(ctl.get(), TERMINATED))
return true;
if (nanos <= 0)
return false;
nanos = termination.awaitNanos(nanos);
}
}
finally
{
mainLock.unlock();
}
}
/**
* Invokes {@code shutdown} when this executor is no longer referenced and
* it has no threads.
*/
protected void finalize()
{
shutdown();
}
/**
* Sets the thread factory used to create new threads.
*
* @param threadFactory
* the new thread factory
* @throws NullPointerException
* if threadFactory is null
* @see #getThreadFactory
*/
public void setThreadFactory(ThreadFactory threadFactory)
{
if (threadFactory == null)
throw new NullPointerException();
this.threadFactory = threadFactory;
}
/**
* Returns the thread factory used to create new threads.
*
* @return the current thread factory
* @see #setThreadFactory
*/
public ThreadFactory getThreadFactory()
{
return threadFactory;
}
/**
* Sets a new handler for unexecutable tasks.
*
* @param handler
* the new handler
* @throws NullPointerException
* if handler is null
* @see #getRejectedExecutionHandler
*/
public void setRejectedExecutionHandler(RejectedExecutionHandler handler)
{
if (handler == null)
throw new NullPointerException();
this.handler = handler;
}
/**
* Returns the current handler for unexecutable tasks.
*
* @return the current handler
* @see #setRejectedExecutionHandler
*/
public RejectedExecutionHandler getRejectedExecutionHandler()
{
return handler;
}
/**
* Sets the core number of threads. This overrides any value set in the
* constructor. If the new value is smaller than the current value, excess
* existing threads will be terminated when they next become idle. If
* larger, new threads will, if needed, be started to execute any queued
* tasks.
*
* @param corePoolSize
* the new core size
* @throws IllegalArgumentException
* if {@code corePoolSize < 0}
* @see #getCorePoolSize
*/
public void setCorePoolSize(int corePoolSize)
{
if (corePoolSize < 0)
throw new IllegalArgumentException();
int delta = corePoolSize - this.corePoolSize;
this.corePoolSize = corePoolSize;
if (workerCountOf(ctl.get()) > corePoolSize)
interruptIdleWorkers();
else if (delta > 0)
{
// We don't really know how many new threads are "needed".
// As a heuristic, prestart enough new workers (up to new
// core size) to handle the current number of tasks in
// queue, but stop if queue becomes empty while doing so.
int k = Math.min(delta, workQueue.size());
while (k-- > 0 && addWorker(null, true))
{
if (workQueue.isEmpty())
break;
}
}
}
/**
* Returns the core number of threads.
*
* @return the core number of threads
* @see #setCorePoolSize
*/
public int getCorePoolSize()
{
return corePoolSize;
}
/**
* Starts a core thread, causing it to idly wait for work. This overrides
* the default policy of starting core threads only when new tasks are
* executed. This method will return {@code false} if all core threads have
* already been started.
*
* @return {@code true} if a thread was started
*/
public boolean prestartCoreThread()
{
return workerCountOf(ctl.get()) < corePoolSize && addWorker(null, true);
}
/**
* Starts all core threads, causing them to idly wait for work. This
* overrides the default policy of starting core threads only when new tasks
* are executed.
*
* @return the number of threads started
*/
public int prestartAllCoreThreads()
{
int n = 0;
while (addWorker(null, true))
++n;
return n;
}
/**
* Returns true if this pool allows core threads to time out and terminate
* if no tasks arrive within the keepAlive time, being replaced if needed
* when new tasks arrive. When true, the same keep-alive policy applying to
* non-core threads applies also to core threads. When false (the default),
* core threads are never terminated due to lack of incoming tasks.
*
* @return {@code true} if core threads are allowed to time out, else
* {@code false}
*
* @since 1.6
*/
public boolean allowsCoreThreadTimeOut()
{
return allowCoreThreadTimeOut;
}
/**
* Sets the policy governing whether core threads may time out and terminate
* if no tasks arrive within the keep-alive time, being replaced if needed
* when new tasks arrive. When false, core threads are never terminated due
* to lack of incoming tasks. When true, the same keep-alive policy applying
* to non-core threads applies also to core threads. To avoid continual
* thread replacement, the keep-alive time must be greater than zero when
* setting {@code true}. This method should in general be called before the
* pool is actively used.
*
* @param value
* {@code true} if should time out, else {@code false}
* @throws IllegalArgumentException
* if value is {@code true} and the current keep-alive time is
* not greater than zero
*
* @since 1.6
*/
public void allowCoreThreadTimeOut(boolean value)
{
if (value && keepAliveTime <= 0)
throw new IllegalArgumentException(
"Core threads must have nonzero keep alive times");
if (value != allowCoreThreadTimeOut)
{
allowCoreThreadTimeOut = value;
if (value)
interruptIdleWorkers();
}
}
/**
* Sets the maximum allowed number of threads. This overrides any value set
* in the constructor. If the new value is smaller than the current value,
* excess existing threads will be terminated when they next become idle.
*
* @param maximumPoolSize
* the new maximum
* @throws IllegalArgumentException
* if the new maximum is less than or equal to zero, or less
* than the {@linkplain #getCorePoolSize core pool size}
* @see #getMaximumPoolSize
*/
public void setMaximumPoolSize(int maximumPoolSize)
{
if (maximumPoolSize <= 0 || maximumPoolSize < corePoolSize)
throw new IllegalArgumentException();
this.maximumPoolSize = maximumPoolSize;
if (workerCountOf(ctl.get()) > maximumPoolSize)
interruptIdleWorkers();
}
/**
* Returns the maximum allowed number of threads.
*
* @return the maximum allowed number of threads
* @see #setMaximumPoolSize
*/
public int getMaximumPoolSize()
{
return maximumPoolSize;
}
/**
* Sets the time limit for which threads may remain idle before being
* terminated. If there are more than the core number of threads currently
* in the pool, after waiting this amount of time without processing a task,
* excess threads will be terminated. This overrides any value set in the
* constructor.
*
* @param time
* the time to wait. A time value of zero will cause excess
* threads to terminate immediately after executing tasks.
* @param unit
* the time unit of the {@code time} argument
* @throws IllegalArgumentException
* if {@code time} less than zero or if {@code time} is zero and
* {@code allowsCoreThreadTimeOut}
* @see #getKeepAliveTime
*/
public void setKeepAliveTime(long time, TimeUnit unit)
{
if (time < 0)
throw new IllegalArgumentException();
if (time == 0 && allowsCoreThreadTimeOut())
throw new IllegalArgumentException(
"Core threads must have nonzero keep alive times");
long keepAliveTime = unit.toNanos(time);
long delta = keepAliveTime - this.keepAliveTime;
this.keepAliveTime = keepAliveTime;
if (delta < 0)
interruptIdleWorkers();
}
/**
* Returns the thread keep-alive time, which is the amount of time that
* threads in excess of the core pool size may remain idle before being
* terminated.
*
* @param unit
* the desired time unit of the result
* @return the time limit
* @see #setKeepAliveTime
*/
public long getKeepAliveTime(TimeUnit unit)
{
return unit.convert(keepAliveTime, TimeUnit.NANOSECONDS);
}
/* User-level queue utilities */
/**
* Returns the task queue used by this executor. Access to the task queue is
* intended primarily for debugging and monitoring. This queue may be in
* active use. Retrieving the task queue does not prevent queued tasks from
* executing.
*
* @return the task queue
*/
public BlockingQueue<Runnable> getQueue()
{
return workQueue;
}
/**
* Removes this task from the executor's internal queue if it is present,
* thus causing it not to be run if it has not already started.
*
* <p>
* This method may be useful as one part of a cancellation scheme. It may
* fail to remove tasks that have been converted into other forms before
* being placed on the internal queue. For example, a task entered using
* {@code submit} might be converted into a form that maintains
* {@code Future} status. However, in such cases, method {@link #purge} may
* be used to remove those Futures that have been cancelled.
*
* @param task
* the task to remove
* @return true if the task was removed
*/
public boolean remove(Runnable task)
{
boolean removed = workQueue.remove(task);
tryTerminate(); // In case SHUTDOWN and now empty
return removed;
}
/**
* Tries to remove from the work queue all {@link Future} tasks that have
* been cancelled. This method can be useful as a storage reclamation
* operation, that has no other impact on functionality. Cancelled tasks are
* never executed, but may accumulate in work queues until worker threads
* can actively remove them. Invoking this method instead tries to remove
* them now. However, this method may fail to remove tasks in the presence
* of interference by other threads.
*/
public void purge()
{
final BlockingQueue<Runnable> q = workQueue;
try
{
Iterator<Runnable> it = q.iterator();
while (it.hasNext())
{
Runnable r = it.next();
if (r instanceof Future<?> && ((Future<?>) r).isCancelled())
it.remove();
}
}
catch (ConcurrentModificationException fallThrough)
{
// Take slow path if we encounter interference during traversal.
// Make copy for traversal and call remove for cancelled entries.
// The slow path is more likely to be O(N*N).
for (Object r : q.toArray())
if (r instanceof Future<?> && ((Future<?>) r).isCancelled())
q.remove(r);
}
tryTerminate(); // In case SHUTDOWN and now empty
}
/* Statistics */
/**
* Returns the current number of threads in the pool.
*
* @return the number of threads
*/
public int getPoolSize()
{
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try
{
// Remove rare and surprising possibility of
// isTerminated() && getPoolSize() > 0
return runStateAtLeast(ctl.get(), TIDYING) ? 0 : workers.size();
}
finally
{
mainLock.unlock();
}
}
/**
* Returns the approximate number of threads that are actively executing
* tasks.
*
* @return the number of threads
*/
public int getActiveCount()
{
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try
{
int n = 0;
for (Worker w : workers)
if (w.isLocked())
++n;
return n;
}
finally
{
mainLock.unlock();
}
}
/**
* Returns the largest number of threads that have ever simultaneously been
* in the pool.
*
* @return the number of threads
*/
public int getLargestPoolSize()
{
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try
{
return largestPoolSize;
}
finally
{
mainLock.unlock();
}
}
/**
* Returns the approximate total number of tasks that have ever been
* scheduled for execution. Because the states of tasks and threads may
* change dynamically during computation, the returned value is only an
* approximation.
*
* @return the number of tasks
*/
public long getTaskCount()
{
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try
{
long n = completedTaskCount;
for (Worker w : workers)
{
n += w.completedTasks;
if (w.isLocked())
++n;
}
return n + workQueue.size();
}
finally
{
mainLock.unlock();
}
}
/**
* Returns the approximate total number of tasks that have completed
* execution. Because the states of tasks and threads may change dynamically
* during computation, the returned value is only an approximation, but one
* that does not ever decrease across successive calls.
*
* @return the number of tasks
*/
public long getCompletedTaskCount()
{
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try
{
long n = completedTaskCount;
for (Worker w : workers)
n += w.completedTasks;
return n;
}
finally
{
mainLock.unlock();
}
}
/* Extension hooks */
/**
* Method invoked prior to executing the given Runnable in the given thread.
* This method is invoked by thread {@code t} that will execute task
* {@code r}, and may be used to re-initialize ThreadLocals, or to perform
* logging.
*
* <p>
* This implementation does nothing, but may be customized in subclasses.
* Note: To properly nest multiple overridings, subclasses should generally
* invoke {@code super.beforeExecute} at the end of this method.
*
* @param t
* the thread that will run task {@code r}
* @param r
* the task that will be executed
*/
protected void beforeExecute(Thread t, Runnable r)
{
}
/**
* Method invoked upon completion of execution of the given Runnable. This
* method is invoked by the thread that executed the task. If non-null, the
* Throwable is the uncaught {@code RuntimeException} or {@code Error} that
* caused execution to terminate abruptly.
*
* <p>
* This implementation does nothing, but may be customized in subclasses.
* Note: To properly nest multiple overridings, subclasses should generally
* invoke {@code super.afterExecute} at the beginning of this method.
*
* <p>
* <b>Note:</b> When actions are enclosed in tasks (such as
* {@link FutureTask}) either explicitly or via methods such as
* {@code submit}, these task objects catch and maintain computational
* exceptions, and so they do not cause abrupt termination, and the internal
* exceptions are <em>not</em> passed to this method. If you would like to
* trap both kinds of failures in this method, you can further probe for
* such cases, as in this sample subclass that prints either the direct
* cause or the underlying exception if a task has been aborted:
*
* <pre>
* {
* @code
* class ExtendedExecutor extends ContinuationsExecutor
* {
* // ...
* protected void afterExecute(Runnable r, Throwable t)
* {
* super.afterExecute(r, t);
* if (t == null && r instanceof Future<?>)
* {
* try
* {
* Object result = ((Future<?>) r).get();
* }
* catch (CancellationException ce)
* {
* t = ce;
* }
* catch (ExecutionException ee)
* {
* t = ee.getCause();
* }
* catch (InterruptedException ie)
* {
* Thread.currentThread().interrupt(); // ignore/reset
* }
* }
* if (t != null)
* System.out.println(t);
* }
* }
* }
* </pre>
*
* @param r
* the runnable that has completed
* @param t
* the exception that caused termination, or null if execution
* completed normally
*/
protected void afterExecute(Runnable r, Throwable t)
{
if ( t != null )
logger_.info( LogUtil.throwableToString(t) );
}
/**
* Method invoked when the Executor has terminated. Default implementation
* does nothing. Note: To properly nest multiple overridings, subclasses
* should generally invoke {@code super.terminated} within this method.
*/
protected void terminated()
{
}
/* Predefined RejectedExecutionHandlers */
/**
* A handler for rejected tasks that runs the rejected task directly in the
* calling thread of the {@code execute} method, unless the executor has
* been shut down, in which case the task is discarded.
*/
public static class CallerRunsPolicy implements RejectedExecutionHandler
{
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy()
{
}
/**
* Executes task r in the caller's thread, unless the executor has been
* shut down, in which case the task is discarded.
*
* @param r
* the runnable task requested to be executed
* @param e
* the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ContinuationsExecutor e)
{
if (!e.isShutdown())
{
r.run();
}
}
}
/**
* A handler for rejected tasks that throws a
* {@code RejectedExecutionException}.
*/
public static class AbortPolicy implements RejectedExecutionHandler
{
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy()
{
}
/**
* Always throws RejectedExecutionException.
*
* @param r
* the runnable task requested to be executed
* @param e
* the executor attempting to execute this task
* @throws RejectedExecutionException
* always.
*/
public void rejectedExecution(Runnable r, ContinuationsExecutor e)
{
throw new RejectedExecutionException();
}
}
/**
* A handler for rejected tasks that silently discards the rejected task.
*/
public static class DiscardPolicy implements RejectedExecutionHandler
{
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy()
{
}
/**
* Does nothing, which has the effect of discarding task r.
*
* @param r
* the runnable task requested to be executed
* @param e
* the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ContinuationsExecutor e)
{
}
}
/**
* A handler for rejected tasks that discards the oldest unhandled request
* and then retries {@code execute}, unless the executor is shut down, in
* which case the task is discarded.
*/
public static class DiscardOldestPolicy implements RejectedExecutionHandler
{
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy()
{
}
/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ContinuationsExecutor e)
{
if (!e.isShutdown())
{
e.getQueue().poll();
e.execute(r);
}
}
}
}