/** * 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. */ /* * 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/licenses/publicdomain */ package org.fusesource.hawtdispatch.internal.pool; import org.fusesource.hawtdispatch.Task; import org.fusesource.hawtdispatch.internal.WorkerPool; import org.fusesource.hawtdispatch.internal.WorkerThread; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; /** * A work stealing worker pool on the ForkJoinPool by Doug Lea. * * @author Doug Lea */ final public class StealingPool implements WorkerPool { /* * See the extended comments interspersed below for design, * rationale, and walkthroughs. */ /** Mask for packing and unpacking shorts */ private static final int shortMask = 0xffff; /** Max pool size -- must be a power of two minus 1 */ private static final int MAX_THREADS = 0x7FFF; /** * Generator for assigning sequence numbers as pool names. */ private static final AtomicInteger poolNumberGenerator = new AtomicInteger(); /** * Array holding all worker threads in the pool. Initialized upon * first use. Array size must be a power of two. Updates and * replacements are protected by workerLock, but it is always kept * in a consistent enough state to be randomly accessed without * locking by threads performing work-stealing. */ volatile StealingThread[] threads; /** * Lock protecting access to threads. */ private final ReentrantLock workerLock; /** * Condition for awaitTermination. */ private final Condition termination; /** * Sum of per-thread steal counts, updated only when threads are * idle or terminating. */ private final AtomicLong stealCount; /** * Queue for external submissions. */ private final LinkedTransferQueue<Task> submissionQueue; /** * Head of Treiber stack for barrier sync. See below for explanation. */ private volatile WaitQueueNode syncStack; /** * The count for event barrier */ private volatile long eventCount; /** * The desired parallelism level. */ private final int parallelism; /** * The desired priority level of the threads */ private final int priority; /** * The name of the pool */ private final String name; /** * Holds number of total (i.e., created and not yet terminated) * and running (i.e., not blocked on joins or other managed sync) * threads, packed into one int to ensure consistent snapshot when * making decisions about creating and suspending spare * threads. Updated only by CAS. Note: CASes in * updateRunningCount and preJoin assume that running active count * is in low word, so need to be modified if this changes. */ private volatile int workerCounts; private static int totalCountOf(int s) { return s >>> 16; } private static int runningCountOf(int s) { return s & shortMask; } private static int workerCountsFor(int t, int r) { return (t << 16) + r; } /** * Adds delta (which may be negative) to running count. This must * be called before (with negative arg) and after (with positive) * any managed synchronization (i.e., mainly, joins). * * @param delta the number to add */ final void updateRunningCount(int delta) { int s; do {} while (!casWorkerCounts(s = workerCounts, s + delta)); } /** * Adds delta (which may be negative) to both total and running * count. This must be called upon creation and termination of * worker threads. * * @param delta the number to add */ private void updateWorkerCount(int delta) { int d = delta + (delta << 16); // add to both lo and hi parts int s; do {} while (!casWorkerCounts(s = workerCounts, s + d)); } /** * Lifecycle control. High word contains runState, low word * contains the number of threads that are (probably) executing * tasks. This value is atomically incremented before a worker * gets a task to run, and decremented when worker has no tasks * and cannot find any. These two fields are bundled together to * support correct termination triggering. Note: activeCount * CAS'es cheat by assuming active count is in low word, so need * to be modified if this changes */ private volatile int runControl; // RunState values. Order among values matters private static final int RUNNING = 0; private static final int SHUTDOWN = 1; private static final int TERMINATING = 2; private static final int TERMINATED = 3; private static int runStateOf(int c) { return c >>> 16; } private static int activeCountOf(int c) { return c & shortMask; } private static int runControlFor(int r, int a) { return (r << 16) + a; } /** * Tries incrementing active count; fails on contention. * Called by threads before/during executing tasks. * * @return true on success */ final boolean tryIncrementActiveCount() { int c = runControl; return casRunControl(c, c+1); } /** * Tries decrementing active count; fails on contention. * Possibly triggers termination on success. * Called by threads when they can't find tasks. * * @return true on success */ final boolean tryDecrementActiveCount() { int c = runControl; int nextc = c - 1; if (!casRunControl(c, nextc)) return false; if (canTerminateOnShutdown(nextc)) terminateOnShutdown(); return true; } /** * Returns {@code true} if argument represents zero active count * and nonzero runstate, which is the triggering condition for * terminating on shutdown. */ private static boolean canTerminateOnShutdown(int c) { // i.e. least bit is nonzero runState bit return ((c & -c) >>> 16) != 0; } /** * Transition run state to at least the given state. Return true * if not already at least given state. */ private boolean transitionRunStateTo(int state) { for (;;) { int c = runControl; if (runStateOf(c) >= state) return false; if (casRunControl(c, runControlFor(state, activeCountOf(c)))) return true; } } // Constructors /** * Creates a {@code ForkJoinPool} with parallelism equal to {@link * java.lang.Runtime#availableProcessors}. * * @throws SecurityException if a security manager exists and * the caller is not permitted to modify threads * because it does not hold {@link * java.lang.RuntimePermission}{@code ("modifyThread")} */ public StealingPool() { this("WorkerPool-" + poolNumberGenerator.incrementAndGet(), Runtime.getRuntime().availableProcessors(), Thread.NORM_PRIORITY); } /** * Creates a {@code ForkJoinPool} with the given parallelism . * * @param parallelism the parallelism level * @throws IllegalArgumentException if parallelism less than or * equal to zero, or greater than implementation limit */ public StealingPool(String name, int parallelism, int priority) { if (parallelism <= 0 || parallelism > MAX_THREADS) throw new IllegalArgumentException(); this.name = name; this.parallelism = parallelism; this.priority=priority; this.workerLock = new ReentrantLock(); this.termination = workerLock.newCondition(); this.stealCount = new AtomicLong(); this.submissionQueue = new LinkedTransferQueue<Task>(); threads = new StealingThread[parallelism]; for (int i = 0; i < parallelism; ++i) { threads[i] = createWorker(i); } } /** * * @param index the index to assign worker * @return new worker, or null if failed */ private StealingThread createWorker(int index) { StealingThread w; try { w = new StealingThread(this); } catch (Exception e) { throw new RuntimeException(e); } w.poolIndex = index; w.setDaemon(true); w.setPriority(priority); w.setName(name + "-worker-" + index); return w; } /** * Returns a good size for worker array given pool size. * Currently requires size to be a power of two. */ private static int arraySizeFor(int poolSize) { if (poolSize <= 1) return 1; // See Hackers Delight, sec 3.2 int c = poolSize >= MAX_THREADS ? MAX_THREADS : (poolSize - 1); c |= c >>> 1; c |= c >>> 2; c |= c >>> 4; c |= c >>> 8; c |= c >>> 16; return c + 1; } /** * Creates or resizes array if necessary to hold newLength. * Call only under exclusion. * * @return the array */ private StealingThread[] ensureWorkerArrayCapacity(int newLength) { StealingThread[] ws = threads; if (ws == null) return threads = new StealingThread[arraySizeFor(newLength)]; else if (newLength > ws.length) return threads = Arrays.copyOf(ws, arraySizeFor(newLength)); else return ws; } /** * Tries to shrink threads into smaller array after one or more terminate. */ private void tryShrinkWorkerArray() { StealingThread[] ws = threads; if (ws != null) { int len = ws.length; int last = len - 1; while (last >= 0 && ws[last] == null) --last; int newLength = arraySizeFor(last+1); if (newLength < len) threads = Arrays.copyOf(ws, newLength); } } /** * start the threads */ public final void start() { for (int i = 0; i < threads.length; ++i) { threads[i].start(); } } // Execution methods /** * Common code for execute, invoke and submit */ private void doSubmit(Task task) { if (task == null) throw new NullPointerException(); if (isShutdown()) throw new RejectedExecutionException(); submissionQueue.offer(task); signalIdleWorkers(); } /** * Arranges for (asynchronous) execution of the given task. * * @param task the task * @throws NullPointerException if the task is null * @throws RejectedExecutionException if the task cannot be * scheduled for execution */ public void execute(Task task) { doSubmit(task); } public WorkerThread[] getThreads() { return threads; } /** * Returns the targeted parallelism level of this pool. * * @return the targeted parallelism level of this pool */ public int getParallelism() { return parallelism; } /** * Returns an estimate of the number of worker threads that are * not blocked waiting to join tasks or for other managed * synchronization. * * @return the number of worker threads */ public int getRunningThreadCount() { return runningCountOf(workerCounts); } /** * Returns an estimate of the number of threads that are currently * stealing or executing tasks. This method may overestimate the * number of active threads. * * @return the number of active threads */ public int getActiveThreadCount() { return activeCountOf(runControl); } /** * Returns an estimate of the number of threads that are currently * idle waiting for tasks. This method may underestimate the * number of idle threads. * * @return the number of idle threads */ final int getIdleThreadCount() { int c = runningCountOf(workerCounts) - activeCountOf(runControl); return (c <= 0) ? 0 : c; } /** * Returns {@code true} if all worker threads are currently idle. * An idle worker is one that cannot obtain a task to execute * because none are available to steal from other threads, and * there are no pending submissions to the pool. This method is * conservative; it might not return {@code true} immediately upon * idleness of all threads, but will eventually become true if * threads remain inactive. * * @return {@code true} if all threads are currently idle */ public boolean isQuiescent() { return activeCountOf(runControl) == 0; } /** * Returns an estimate of the total number of tasks stolen from * one thread's work queue by another. The reported value * underestimates the actual total number of steals when the pool * is not quiescent. This value may be useful for monitoring and * tuning fork/join programs: in general, steal counts should be * high enough to keep threads busy, but low enough to avoid * overhead and contention across threads. * * @return the number of steals */ public long getStealCount() { return stealCount.get(); } /** * Accumulates steal count from a worker. * Call only when worker known to be idle. */ private void updateStealCount(StealingThread w) { int sc = w.getAndClearStealCount(); if (sc != 0) stealCount.addAndGet(sc); } /** * Returns an estimate of the total number of tasks currently held * in queues by worker threads (but not including tasks submitted * to the pool that have not begun executing). This value is only * an approximation, obtained by iterating across all threads in * the pool. This method may be useful for tuning task * granularities. * * @return the number of queued tasks */ public long getQueuedTaskCount() { long count = 0; StealingThread[] ws = threads; if (ws != null) { for (int i = 0; i < ws.length; ++i) { StealingThread t = ws[i]; if (t != null) count += t.getQueueSize(); } } return count; } /** * Returns an estimate of the number of tasks submitted to this * pool that have not yet begun executing. This method takes time * proportional to the number of submissions. * * @return the number of queued submissions */ public int getQueuedSubmissionCount() { return submissionQueue.size(); } /** * Returns {@code true} if there are any tasks submitted to this * pool that have not yet begun executing. * * @return {@code true} if there are any queued submissions */ public boolean hasQueuedSubmissions() { return !submissionQueue.isEmpty(); } /** * Removes and returns the next unexecuted submission if one is * available. This method may be useful in extensions to this * class that re-assign work in systems with multiple pools. * * @return the next submission, or {@code null} if none */ protected Task pollSubmission() { return submissionQueue.poll(); } /** * Removes all available unexecuted submitted and forked tasks * from scheduling queues and adds them to the given collection, * without altering their execution status. These may include * artificially generated or wrapped tasks. This method is * designed to be invoked only when the pool is known to be * quiescent. Invocations at other times may not remove all * tasks. A failure encountered while attempting to add elements * to collection {@code c} may result in elements being in * neither, either or both collections when the associated * exception is thrown. The behavior of this operation is * undefined if the specified collection is modified while the * operation is in progress. * * @param c the collection to transfer elements into * @return the number of elements transferred */ protected int drainTasksTo(Collection<? super Task> c) { int n = submissionQueue.drainTo(c); StealingThread[] ws = threads; if (ws != null) { for (int i = 0; i < ws.length; ++i) { StealingThread w = ws[i]; if (w != null) n += w.drainTasksTo(c); } } return n; } /** * Returns a string identifying this pool, as well as its state, * including indications of run state, parallelism level, and * worker and task counts. * * @return a string identifying this pool, as well as its state */ public String toString() { int ps = parallelism; int wc = workerCounts; int rc = runControl; long st = getStealCount(); long qt = getQueuedTaskCount(); long qs = getQueuedSubmissionCount(); return super.toString() + "[" + runStateToString(runStateOf(rc)) + ", parallelism = " + ps + ", size = " + totalCountOf(wc) + ", active = " + activeCountOf(rc) + ", running = " + runningCountOf(wc) + ", steals = " + st + ", tasks = " + qt + ", submissions = " + qs + "]"; } private static String runStateToString(int rs) { switch (rs) { case RUNNING: return "Running"; case SHUTDOWN: return "Shutting down"; case TERMINATING: return "Terminating"; case TERMINATED: return "Terminated"; default: throw new Error("Unknown run state"); } } // lifecycle control /** * 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. * Tasks that are in the process of being submitted concurrently * during the course of this method may or may not be rejected. * */ public void shutdown() { transitionRunStateTo(SHUTDOWN); if (canTerminateOnShutdown(runControl)) { if (threads == null) { // shutting down before threads created final ReentrantLock lock = this.workerLock; lock.lock(); try { if (threads == null) { terminate(); transitionRunStateTo(TERMINATED); termination.signalAll(); } } finally { lock.unlock(); } } terminateOnShutdown(); } } /** * Attempts to cancel and/or stop all tasks, and reject all * subsequently submitted tasks. Tasks that are in the process of * being submitted or executed concurrently during the course of * this method may or may not be rejected. This method cancels * both existing and unexecuted tasks, in order to permit * termination in the presence of task dependencies. So the method * always returns an empty list (unlike the case for some other * Executors). * * @return an empty list */ public List<Task> shutdownNow() { terminate(); return Collections.emptyList(); } /** * Returns {@code true} if all tasks have completed following shut down. * * @return {@code true} if all tasks have completed following shut down */ public boolean isTerminated() { return runStateOf(runControl) == TERMINATED; } /** * Returns {@code true} if the process of termination has * commenced but not yet completed. 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 {@code true} if terminating but not yet terminated */ public boolean isTerminating() { return runStateOf(runControl) == TERMINATING; } /** * Returns {@code true} if this pool has been shut down. * * @return {@code true} if this pool has been shut down */ public boolean isShutdown() { return runStateOf(runControl) >= SHUTDOWN; } /** * Returns true if pool is not terminating or terminated. * Used internally to suppress execution when terminating. */ final boolean isProcessingTasks() { return runStateOf(runControl) < TERMINATING; } /** * Blocks until all tasks have completed execution after a shutdown * request, or the timeout occurs, or the current thread is * interrupted, whichever happens first. * * @param timeout the maximum time to wait * @param unit the time unit of the timeout argument * @return {@code true} if this executor terminated and * {@code false} if the timeout elapsed before termination * @throws InterruptedException if interrupted while waiting */ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.workerLock; lock.lock(); try { for (;;) { if (isTerminated()) return true; if (nanos <= 0) return false; nanos = termination.awaitNanos(nanos); } } finally { lock.unlock(); } } // Shutdown and termination support /** * Callback from terminating worker. Nulls out the corresponding * threads slot, and if terminating, tries to terminate; else * tries to shrink threads array. * * @param w the worker */ final void workerTerminated(StealingThread w) { updateStealCount(w); updateWorkerCount(-1); final ReentrantLock lock = this.workerLock; lock.lock(); try { StealingThread[] ws = threads; if (ws != null) { int idx = w.poolIndex; if (idx >= 0 && idx < ws.length && ws[idx] == w) ws[idx] = null; if (totalCountOf(workerCounts) == 0) { terminate(); // no-op if already terminating transitionRunStateTo(TERMINATED); termination.signalAll(); } // We should only loose works when terminating. assert !isProcessingTasks(); } } finally { lock.unlock(); } signalIdleWorkers(); } /** * Initiates termination. */ private void terminate() { if (transitionRunStateTo(TERMINATING)) { stopAllWorkers(); signalIdleWorkers(); interruptUnterminatedWorkers(); signalIdleWorkers(); // resignal after interrupt } } /** * Possibly terminates when on shutdown state. */ private void terminateOnShutdown() { if (!hasQueuedSubmissions() && canTerminateOnShutdown(runControl)) terminate(); } /** * Sets each worker's status to terminating. Requires lock to avoid * conflicts with add/remove. */ private void stopAllWorkers() { final ReentrantLock lock = this.workerLock; lock.lock(); try { StealingThread[] ws = threads; if (ws != null) { for (int i = 0; i < ws.length; ++i) { StealingThread t = ws[i]; if (t != null) t.shutdownNow(); } } } finally { lock.unlock(); } } /** * Interrupts all unterminated threads. This is not required for * sake of internal control, but may help unstick user code during * shutdown. */ private void interruptUnterminatedWorkers() { final ReentrantLock lock = this.workerLock; lock.lock(); try { StealingThread[] ws = threads; if (ws != null) { for (int i = 0; i < ws.length; ++i) { StealingThread t = ws[i]; if (t != null && !t.isTerminated()) { try { t.interrupt(); } catch (SecurityException ignore) { } } } } } finally { lock.unlock(); } } /* * Nodes for event barrier to manage idle threads. Queue nodes * are basic Treiber stack nodes, also used for spare stack. * * The event barrier has an event count and a wait queue (actually * a Treiber stack). Workers are enabled to look for work when * the eventCount is incremented. If they fail to find work, they * may wait for next count. Upon release, threads help others wake * up. * * Synchronization events occur only in enough contexts to * maintain overall liveness: * * - Submission of a new task to the pool * - Resizes or other changes to the threads array * - pool termination * - A worker pushing a task on an empty queue * * The case of pushing a task occurs often enough, and is heavy * enough compared to simple stack pushes, to require special * handling: Method signalWork returns without advancing count if * the queue appears to be empty. This would ordinarily result in * races causing some queued waiters not to be woken up. To avoid * this, the first worker enqueued in method sync rescans for * tasks after being enqueued, and helps signal if any are * found. This works well because the worker has nothing better to * do, and so might as well help alleviate the overhead and * contention on the threads actually doing work. Also, since * event counts increments on task availability exist to maintain * liveness (rather than to force refreshes etc), it is OK for * callers to exit early if contending with another signaller. */ static final class WaitQueueNode { WaitQueueNode next; // only written before enqueued volatile StealingThread thread; // nulled to cancel wait final long count; // unused for spare stack WaitQueueNode(long c, StealingThread w) { count = c; thread = w; } /** * Wakes up waiter, also clearing thread field */ void signal() { StealingThread t = thread; if (t != null) { thread = null; t.unpark(); } } } /** * Ensures that no thread is waiting for count to advance from the * current value of eventCount read on entry to this method, by * releasing waiting threads if necessary. */ final void ensureSync() { long c = eventCount; WaitQueueNode q; while ((q = syncStack) != null && q.count < c) { if (casBarrierStack(q, null)) { do { q.signal(); } while ((q = q.next) != null); break; } } } /** * Increments event count and releases waiting threads. */ private void signalIdleWorkers() { long c; do {} while (!casEventCount(c = eventCount, c+1)); ensureSync(); } /** * Signals threads waiting to poll a task. Because method sync * rechecks availability, it is OK to only proceed if queue * appears to be non-empty, and OK if CAS to increment count * fails (since some other thread succeeded). */ final void signalWork() { if (syncStack != null) { long c = eventCount; casEventCount(c, c+1); WaitQueueNode q = syncStack; if (q != null && q.count <= c) { if (casBarrierStack(q, q.next)) q.signal(); else ensureSync(); // awaken all on contention } } } /** * Possibly blocks until event count advances from last value held * by caller, or if excess threads, caller is resumed as spare, or * caller or pool is terminating. Updates caller's event on exit. * * @param w the calling worker thread */ final void sync(StealingThread w) { updateStealCount(w); // Transfer w's count while it is idle if (!w.isShutdown() && isProcessingTasks()) { long prev = w.lastEventCount; WaitQueueNode node = null; WaitQueueNode h; long c; while ((c = eventCount) == prev && ((h = syncStack) == null || h.count == prev)) { if (node == null) node = new WaitQueueNode(prev, w); if (casBarrierStack(node.next = h, node)) { if (!Thread.interrupted() && node.thread != null && eventCount == prev && (h != null || // cover signalWork race (!StealingThread.hasQueuedTasks(threads) && eventCount == prev))) w.park(); c = eventCount; if (node.thread != null) { // help signal if not unparked node.thread = null; if (c == prev) casEventCount(prev, prev + 1); } break; } } w.lastEventCount = c; ensureSync(); } } /** * Returns {@code true} if a new sync event occurred since last * call to sync or this method, if so, updating caller's count. */ final boolean hasNewSyncEvent(StealingThread w) { long wc = w.lastEventCount; long c = eventCount; if (wc != c) w.lastEventCount = c; ensureSync(); return wc != c || wc != eventCount; } // Unsafe mechanics static final sun.misc.Unsafe UNSAFE = getUnsafe(); private static final long eventCountOffset = objectFieldOffset("eventCount", StealingPool.class); private static final long workerCountsOffset = objectFieldOffset("workerCounts", StealingPool.class); private static final long runControlOffset = objectFieldOffset("runControl", StealingPool.class); private static final long syncStackOffset = objectFieldOffset("syncStack", StealingPool.class); private boolean casEventCount(long cmp, long val) { return UNSAFE.compareAndSwapLong(this, eventCountOffset, cmp, val); } private boolean casWorkerCounts(int cmp, int val) { return UNSAFE.compareAndSwapInt(this, workerCountsOffset, cmp, val); } private boolean casRunControl(int cmp, int val) { return UNSAFE.compareAndSwapInt(this, runControlOffset, cmp, val); } private boolean casBarrierStack(WaitQueueNode cmp, WaitQueueNode val) { return UNSAFE.compareAndSwapObject(this, syncStackOffset, cmp, val); } private static long objectFieldOffset(String field, Class<?> klazz) { try { return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field)); } catch (NoSuchFieldException e) { // Convert Exception to corresponding Error NoSuchFieldError error = new NoSuchFieldError(field); error.initCause(e); throw error; } } /** * Workaround for not being able to rethrow unchecked exceptions. */ static void rethrowException(Throwable ex) { if (ex != null) StealingPool.UNSAFE.throwException(ex); } /** * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. * Replace with a simple call to Unsafe.getUnsafe when integrating * into a jdk. * * @return a sun.misc.Unsafe */ private static sun.misc.Unsafe getUnsafe() { try { return sun.misc.Unsafe.getUnsafe(); } catch (SecurityException se) { try { return java.security.AccessController.doPrivileged (new java.security .PrivilegedExceptionAction<sun.misc.Unsafe>() { public sun.misc.Unsafe run() throws Exception { java.lang.reflect.Field f = sun.misc .Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); return (sun.misc.Unsafe) f.get(null); }}); } catch (java.security.PrivilegedActionException e) { throw new RuntimeException("Could not initialize intrinsics", e.getCause()); } } } }