//----------------------------------------------------------------------------//
// //
// O m r E x e c u t o r s //
// //
//----------------------------------------------------------------------------//
// <editor-fold defaultstate="collapsed" desc="hdr"> //
// Copyright © Hervé Bitteur and others 2000-2013. All rights reserved. //
// This software is released under the GNU General Public License. //
// Goto http://kenai.com/projects/audiveris to report bugs or suggestions. //
//----------------------------------------------------------------------------//
// </editor-fold>
package omr.util;
import omr.constant.Constant;
import omr.constant.ConstantSet;
import omr.step.ProcessingCancellationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Class {@code OmrExecutors} handles several pools of threads
* provided to the Audiveris application: <ul>
* <li>lowExecutor: a fixed nb (#cpu+1) of threads with low priority</li>
* <li>highExecutor: a fixed nb (#cpu+1) of threads with high priority</li>
* <li>cachedLowExecutor: a varying nb of threads with low priority</li>
* </ul>
*
* @author Hervé Bitteur
*/
public class OmrExecutors
{
//~ Static fields/initializers ---------------------------------------------
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(
OmrExecutors.class);
/** Specific application parameters */
private static final Constants constants = new Constants();
/** Default parameter. */
public static final Param<Boolean> defaultParallelism = new Default();
/** Number of processors available. */
private static final int cpuCount = Runtime.getRuntime()
.availableProcessors();
static {
if (constants.printEnvironment.isSet()) {
logger.info(
"Environment. CPU count: {}, Use of parallelism: {}",
cpuCount,
defaultParallelism.getTarget());
}
}
// Specific pools
private static final Pool highs = new Highs();
private static final Pool lows = new Lows();
private static final Pool cachedLows = new CachedLows();
/** To handle all the pools as a whole */
private static Collection<Pool> allPools = Arrays.asList(
cachedLows,
lows,
highs);
/** To prevent parallel creation of pools when closing */
private static volatile boolean creationAllowed = true;
//~ Constructors -----------------------------------------------------------
/**
* Not meant to be instantiated
*/
private OmrExecutors ()
{
}
//~ Methods ----------------------------------------------------------------
//----------------------//
// getCachedLowExecutor //
//----------------------//
/**
* Return the (single) pool of cached low priority threads
*
* @return the cached low pool, allocated if needed
*/
public static ExecutorService getCachedLowExecutor ()
{
return cachedLows.getPool();
}
//-----------------//
// getHighExecutor //
//-----------------//
/**
* Return the (single) pool of high priority threads
*
* @return the high pool, allocated if needed
*/
public static ExecutorService getHighExecutor ()
{
return highs.getPool();
}
//----------------//
// getLowExecutor //
//----------------//
/**
* Return the (single) pool of low priority threads
*
* @return the low pool, allocated if needed
*/
public static ExecutorService getLowExecutor ()
{
return lows.getPool();
}
//-----------------//
// getNumberOfCpus //
//-----------------//
/**
* Report the number of "processors" available
*
* @return the number of CPUs
*/
public static int getNumberOfCpus ()
{
return cpuCount;
}
//---------//
// restart //
//---------//
/**
* (re-)Allow the creation of pools.
*/
public static void restart ()
{
creationAllowed = true;
logger.debug("OmrExecutors open");
}
//----------//
// shutdown //
//----------//
/**
* Gracefully shut down all the executors launched
*
* @param immediately set to true for an immediate shutdown
*/
public static void shutdown (boolean immediately)
{
logger.debug("Closing all pools ...");
// No creation of pools from now on!
creationAllowed = false;
for (Pool pool : allPools) {
if (pool.isActive()) {
pool.close(immediately);
} else {
logger.debug("Pool {} not active", pool.getName());
}
}
logger.debug("OmrExecutors closed");
}
//~ Inner Classes ----------------------------------------------------------
//------//
// Pool //
//------//
private abstract static class Pool
{
//~ Instance fields ----------------------------------------------------
/** The underlying pool of threads */
protected ExecutorService pool;
//~ Methods ------------------------------------------------------------
/**
* Name the pool.
*/
public abstract String getName ();
//
/**
* Terminate the pool.
*/
public synchronized void close (boolean immediately)
{
if (!isActive()) {
return;
}
logger.debug(
"Closing pool {}{}",
getName(),
immediately ? " immediately" : "");
if (!immediately) {
pool.shutdown(); // Disable new tasks from being submitted
try {
// Wait a while for existing tasks to terminate
if (!pool.awaitTermination(
constants.graceDelay.getValue(),
TimeUnit.SECONDS)) {
// Cancel currently executing tasks
pool.shutdownNow();
logger.warn("[OmrExecutors ] Pool {} did not terminate in {} "
+ "graceDelay period", getName(),
constants.graceDelay.toString());
}
} catch (InterruptedException ie) {
// (Re-)Cancel if current thread also got interrupted
pool.shutdownNow();
// Preserve interrupt status
Thread.currentThread()
.interrupt();
}
} else {
// Cancel currently executing tasks
pool.shutdownNow();
}
logger.debug("Pool {} closed.", getName());
// Let garbage collector work
pool = null;
}
/**
* Get the pool ready to use.
*/
public synchronized ExecutorService getPool ()
{
if (!creationAllowed) {
logger.info("No longer allowed to create pool: {}", getName());
throw new ProcessingCancellationException("Executor closed");
}
if (!isActive()) {
logger.debug("Creating pool: {}", getName());
pool = createPool();
}
return pool;
}
/**
* Is the pool active?.
*/
public synchronized boolean isActive ()
{
return (pool != null) && !pool.isShutdown();
}
/**
* Needed to create the concrete pool.
*/
protected abstract ExecutorService createPool ();
}
//-----------//
// Constants //
//-----------//
private static final class Constants
extends ConstantSet
{
//~ Instance fields ----------------------------------------------------
Constant.Boolean printEnvironment = new Constant.Boolean(
false,
"Should we print out current environment?");
//
Constant.Boolean useParallelism = new Constant.Boolean(
true,
"Should we use parallelism when we have several processors?");
//
Constant.Integer graceDelay = new Constant.Integer(
"seconds",
120, //15,
"Time to wait for terminating tasks");
}
//
//------------//
// CachedLows //
//------------//
/** Cached pool with low priority */
private static class CachedLows
extends Pool
{
//~ Methods ------------------------------------------------------------
@Override
public String getName ()
{
return "cachedLow";
}
@Override
protected ExecutorService createPool ()
{
return Executors.newCachedThreadPool(
new Factory(getName(), Thread.MIN_PRIORITY, 0));
}
}
//---------//
// Default //
//---------//
private static class Default
extends Param<Boolean>
{
//~ Methods ------------------------------------------------------------
@Override
public Boolean getSpecific ()
{
return constants.useParallelism.getValue();
}
@Override
public boolean setSpecific (Boolean specific)
{
if (!getSpecific()
.equals(specific)) {
constants.useParallelism.setValue(specific);
logger.info(
"Parallelism is {} allowed",
specific ? "now" : "no longer");
return true;
} else {
return false;
}
}
}
//---------//
// Factory //
//---------//
private static class Factory
implements ThreadFactory
{
//~ Instance fields ----------------------------------------------------
private final ThreadGroup group;
private final String threadPrefix;
private final int threadPriority;
private final long stackSize;
private final AtomicInteger threadNumber = new AtomicInteger(0);
//~ Constructors -------------------------------------------------------
Factory (String threadPrefix,
int threadPriority,
long stackSize)
{
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup()
: Thread.currentThread()
.getThreadGroup();
this.threadPrefix = threadPrefix;
this.threadPriority = threadPriority;
this.stackSize = stackSize;
}
//~ Methods ------------------------------------------------------------
@Override
public Thread newThread (Runnable r)
{
Thread t = new Thread(group, r, getOneThreadName(), stackSize);
if (t.isDaemon()) {
t.setDaemon(false);
}
if (t.getPriority() != threadPriority) {
t.setPriority(threadPriority);
}
return t;
}
private String getOneThreadName ()
{
return threadPrefix + "-thread-" + threadNumber.incrementAndGet();
}
}
//-------//
// Highs //
//-------//
/** Fixed pool with high priority */
private static class Highs
extends Pool
{
//~ Methods ------------------------------------------------------------
@Override
public String getName ()
{
return "high";
}
@Override
protected ExecutorService createPool ()
{
return Executors.newFixedThreadPool(
defaultParallelism.getTarget() ? (cpuCount + 1) : 1,
new Factory(getName(), Thread.NORM_PRIORITY, 0));
}
}
//------//
// Lows //
//------//
/** Fixed pool with low priority */
private static class Lows
extends Pool
{
//~ Methods ------------------------------------------------------------
@Override
public String getName ()
{
return "low";
}
@Override
protected ExecutorService createPool ()
{
return Executors.newFixedThreadPool(
defaultParallelism.getTarget() ? (cpuCount + 1) : 1,
new Factory(getName(), Thread.MIN_PRIORITY, 0));
}
}
}