/*
* Copyright 2010-2015 Institut Pasteur.
*
* This file is part of Icy.
*
* Icy is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Icy is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Icy. If not, see <http://www.gnu.org/licenses/>.
*/
package icy.system.thread;
import icy.system.IcyExceptionHandler;
import icy.system.SystemUtil;
import java.awt.EventQueue;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import javax.swing.SwingUtilities;
/**
* Thread utilities class.
*
* @author Stephane
*/
public class ThreadUtil
{
/**
* This class is used to catch exception in the EDT.
*/
public static class CaughtRunnable implements Runnable
{
private final Runnable runnable;
public CaughtRunnable(Runnable runnable)
{
super();
this.runnable = runnable;
}
@Override
public void run()
{
try
{
runnable.run();
}
catch (Throwable t)
{
IcyExceptionHandler.handleException(t, true);
}
}
}
/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = Thread.MIN_PRIORITY;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = Thread.NORM_PRIORITY;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = Thread.MAX_PRIORITY;
// low priority background processor
private static final Processor bgProcessor;
// single Runnable / Callable instance processor
private static final InstanceProcessor instanceProcessors[];
// low priority single Runnable / Callable instance processor
private static final InstanceProcessor bgInstanceProcessors[];
static
{
if (SystemUtil.is32bits())
{
int wantedThread = SystemUtil.getNumberOfCPUs();
wantedThread = Math.max(wantedThread, 2);
// 32 bits JVM, limit the number of thread
bgProcessor = new Processor(Math.min(wantedThread, 8));
instanceProcessors = new InstanceProcessor[Math.min(wantedThread, 4)];
bgInstanceProcessors = new InstanceProcessor[Math.min(wantedThread, 4)];
}
else
{
int wantedThread = SystemUtil.getNumberOfCPUs();
wantedThread = Math.max(wantedThread, 4);
// 64 bits JVM, can have higher limit
bgProcessor = new Processor(Math.min(wantedThread, 16));
instanceProcessors = new InstanceProcessor[Math.min(wantedThread, 8)];
bgInstanceProcessors = new InstanceProcessor[Math.min(wantedThread, 8)];
}
bgProcessor.setPriority(MIN_PRIORITY);
bgProcessor.setThreadName("Background processor");
bgProcessor.setKeepAliveTime(3, TimeUnit.SECONDS);
for (int i = 0; i < instanceProcessors.length; i++)
{
// keep these thread active
instanceProcessors[i] = new InstanceProcessor(NORM_PRIORITY);
instanceProcessors[i].setThreadName("Background instance processor");
instanceProcessors[i].setKeepAliveTime(3, TimeUnit.SECONDS);
bgInstanceProcessors[i] = new InstanceProcessor(MIN_PRIORITY);
bgInstanceProcessors[i].setThreadName("Background instance processor (low priority)");
bgInstanceProcessors[i].setKeepAliveTime(3, TimeUnit.SECONDS);
}
}
/**
* Shutdown all background runner.
*/
public static void shutdown()
{
bgProcessor.shutdown();
for (int i = 0; i < instanceProcessors.length; i++)
{
instanceProcessors[i].shutdown();
bgInstanceProcessors[i].shutdown();
}
}
/**
* Return true if all background runner are shutdown and terminated.
*/
public static boolean isShutdownAndTerminated()
{
for (int i = 0; i < instanceProcessors.length; i++)
{
if (!instanceProcessors[i].isTerminated())
return false;
if (!bgInstanceProcessors[i].isTerminated())
return false;
}
return bgProcessor.isTerminated();
}
/**
* @return true if the current thread is an AWT event dispatching thread.
*/
public static boolean isEventDispatchThread()
{
return EventQueue.isDispatchThread();
}
/**
* Invoke the specified <code>Runnable</code> on the AWT event dispatching thread.<br>
* Any exception is automatically caught by Icy exception handler.
*
* @param wait
* If set to true, the method wait until completion, in this case you have to take
* attention to not cause any dead lock.
* @see #invokeLater(Runnable)
* @see #invokeNow(Runnable)
*/
public static void invoke(Runnable runnable, boolean wait)
{
if (wait)
invokeNow(runnable);
else
invokeLater(runnable);
}
/**
* @deprecated Use {@link #invokeNow(Runnable)} instead
*/
@Deprecated
public static void invokeAndWait(Runnable runnable)
{
invokeNow(runnable);
}
/**
* Invoke the specified <code>Runnable</code> on the AWT event dispatching thread now and wait
* until completion.<br>
* Any exception is automatically caught by Icy exception handler, if you want to catch them use
* {@link #invokeNow(Callable)} instead.<br>
* Use this method carefully as it may lead to dead lock.
*/
public static void invokeNow(Runnable runnable)
{
if (isEventDispatchThread())
{
try
{
runnable.run();
}
catch (Throwable t)
{
// the runnable thrown an exception
IcyExceptionHandler.handleException(t, true);
}
}
else
{
try
{
EventQueue.invokeAndWait(runnable);
}
catch (InvocationTargetException e)
{
// the runnable thrown an exception
IcyExceptionHandler.handleException(e.getTargetException(), true);
}
catch (InterruptedException e)
{
// interrupt exception
System.err.println("ThreadUtil.invokeNow(...) error :");
IcyExceptionHandler.showErrorMessage(e, true);
}
}
}
/**
* Invoke the specified <code>Runnable</code> on the AWT event dispatching thread.<br>
* If we already are on the EDT the <code>Runnable</code> is executed immediately else it will
* be executed later.
*
* @see #invokeLater(Runnable, boolean)
*/
public static void invokeLater(Runnable runnable)
{
invokeLater(runnable, false);
}
/**
* Invoke the specified <code>Runnable</code> on the AWT event dispatching thread.<br>
* Depending the <code>forceLater</code> parameter the <code>Runnable</code> can be executed
* immediately if we are on the EDT.
*
* @param forceLater
* If <code>true</code> the <code>Runnable</code> is forced to execute later even if we
* are on the Swing EDT.
*/
public static void invokeLater(Runnable runnable, boolean forceLater)
{
final Runnable r = new CaughtRunnable(runnable);
if ((!forceLater) && isEventDispatchThread())
r.run();
else
EventQueue.invokeLater(r);
}
/**
* Invoke the specified <code>Callable</code> on the AWT event dispatching thread now and return
* the result.<br>
* The returned result can be <code>null</code> when a {@link Throwable} exception happen.<br>
* Use this method carefully as it may lead to dead lock.
*
* @throws InterruptedException
* if the current thread was interrupted while waiting
* @throws Exception
* if the computation threw an exception
*/
public static <T> T invokeNow(Callable<T> callable) throws Exception
{
if (SwingUtilities.isEventDispatchThread())
return callable.call();
final FutureTask<T> task = new FutureTask<T>(callable);
try
{
EventQueue.invokeAndWait(task);
}
catch (InvocationTargetException e)
{
if (e.getTargetException() instanceof Exception)
throw (Exception) e.getTargetException();
throw new Exception(e.getTargetException());
}
try
{
return task.get();
}
catch (ExecutionException e)
{
if (e.getCause() instanceof Exception)
throw (Exception) e.getCause();
throw new Exception(e.getCause());
}
}
/**
* Invoke the specified {@link Callable} on the AWT event dispatching thread.<br>
* Depending the <code>forceLater</code> parameter the <code>Callable</code> can be executed
* immediately if we are on the EDT.
*
* @param forceLater
* If <code>true</code> the <code>Callable</code> is forced to execute later even if we
* are on the EDT.
*/
public static <T> Future<T> invokeLater(Callable<T> callable, boolean forceLater)
{
final FutureTask<T> task = new FutureTask<T>(callable);
invokeLater(task, forceLater);
return task;
}
/**
* Retrieve the instance processor (normal priority) to use for specified runnable.
*/
private static InstanceProcessor getInstanceProcessor(Runnable runnable)
{
// get processor index from the hash code
return instanceProcessors[runnable.hashCode() % instanceProcessors.length];
}
/**
* Retrieve the instance processor (normal priority) to use for specified callable.
*/
private static InstanceProcessor getInstanceProcessor(Callable<?> callable)
{
// get processor index from the hash code
return instanceProcessors[callable.hashCode() % instanceProcessors.length];
}
/**
* Retrieve the instance processor (low priority) to use for specified runnable.
*/
private static InstanceProcessor getBgInstanceProcessor(Runnable runnable)
{
// get processor index from the hash code
return bgInstanceProcessors[runnable.hashCode() % bgInstanceProcessors.length];
}
/**
* Retrieve the instance processor (low priority) to use for specified callable.
*/
private static InstanceProcessor getBgInstanceProcessor(Callable<?> callable)
{
// get processor index from the hash code
return bgInstanceProcessors[callable.hashCode() % bgInstanceProcessors.length];
}
/**
* @deprecated Use {@link #bgRun(Runnable)} instead and {@link #invokeNow(Runnable)} separately.
* @see #bgRun(Runnable)
*/
@Deprecated
public static boolean bgRun(Runnable runnable, boolean onEDT)
{
return (bgProcessor.submit(runnable, onEDT) != null);
}
/**
* @deprecated Use {@link #bgRun(Runnable)} instead and check for acceptance.
*/
@Deprecated
public static void bgRunWait(Runnable runnable)
{
while (!bgRun(runnable))
ThreadUtil.sleep(1);
}
/**
* Adds background processing (low priority) of specified Runnable.<br>
* Returns <code>false</code> if background process queue is full.<br>
* Don't use this method for long process (more than 1 second) as the number of thread is
* limited and others processes may be executed too late.
*/
public static boolean bgRun(Runnable runnable)
{
return (bgProcessor.submit(true, runnable) != null);
}
/**
* @deprecated Use {@link #bgRun(Callable)} and {@link #invokeNow(Callable)} separately instead.
* @see #bgRun(Callable)
*/
@Deprecated
public static <T> Future<T> bgRun(Callable<T> callable, boolean onEDT)
{
return bgProcessor.submit(callable, onEDT);
}
/**
* Adds background processing (low priority) of specified Callable task.<br>
* Returns a Future representing the pending result of the task or <code>null</code> if
* background process queue is full.<br>
* Don't use this method for long process (more than 1 second) as the number of thread is
* limited and others processes may be executed too late.
*/
public static <T> Future<T> bgRun(Callable<T> callable)
{
return bgProcessor.submit(callable);
}
/**
* @deprecated Use {@link #runSingle(Runnable)} instead and {@link #invokeNow(Runnable)} separately.
* @see #bgRunSingle(Runnable)
*/
@Deprecated
public static boolean bgRunSingle(Runnable runnable, boolean onEDT)
{
final InstanceProcessor processor = getInstanceProcessor(runnable);
return (processor.submit(runnable, onEDT) != null);
}
/**
* @deprecated Use {@link #runSingle(Callable)} and {@link #invokeNow(Callable)} separately
* instead.
* @see #bgRunSingle(Callable)
*/
@Deprecated
public static <T> Future<T> bgRunSingle(Callable<T> callable, boolean onEDT)
{
final InstanceProcessor processor = getInstanceProcessor(callable);
return processor.submit(callable, onEDT);
}
/**
* Adds single processing (low priority) of specified Runnable.<br>
* If this <code>Runnable</code> instance is already pending in single processes queue then
* nothing is done.<br>
* Returns <code>false</code> if single processes queue is full.<br>
* Don't use this method for long process (more than 1 second) as the number of thread is
* limited and others processes may be executed too late.
*/
public static boolean bgRunSingle(Runnable runnable)
{
final InstanceProcessor processor = getBgInstanceProcessor(runnable);
return (processor.submit(true, runnable) != null);
}
/**
* Adds single processing (low priority) of specified Callable task.<br>
* If this <code>Callable</code> instance is already pending in single processes queue then
* nothing is done.<br>
* Returns a Future representing the pending result of the task or <code>null</code> if
* single processes queue is full.<br>
* Don't use this method for long process (more than 1 second) as the number of thread is
* limited and others processes may be executed too late.
*/
public static <T> Future<T> bgRunSingle(Callable<T> callable)
{
final InstanceProcessor processor = getBgInstanceProcessor(callable);
return processor.submit(callable);
}
/**
* Add single processing (normal priority) of specified Runnable.<br>
* If this <code>Runnable</code> instance is already pending in single processes queue then
* nothing is done.<br>
* Return <code>false</code> if single processes queue is full.<br>
* Don't use this method for long process (more than 1 second) as the number of thread is
* limited and others processes may be executed too late.
*/
public static boolean runSingle(Runnable runnable)
{
final InstanceProcessor processor = getInstanceProcessor(runnable);
return (processor.submit(true, runnable) != null);
}
/**
* Add single processing (normal priority) of specified Callable task.<br>
* If this <code>Callable</code> instance is already pending in single processes queue then
* nothing is done.<br>
* Return a Future representing the pending result of the task or <code>null</code> if
* single processes queue is full.<br>
* Don't use this method for long process (more than 1 second) as the number of thread is
* limited and others processes may be executed too late.
*/
public static <T> Future<T> runSingle(Callable<T> callable)
{
final InstanceProcessor processor = getInstanceProcessor(callable);
return processor.submit(callable);
}
/**
* Return true if the specified runnable is waiting to be processed in background processing.
*/
public static boolean hasWaitingBgTask(Runnable runnable)
{
return bgProcessor.getWaitingTasksCount(runnable) > 0;
}
/**
* Return true if the specified callable is waiting to be processed in background processing.
*/
public static boolean hasWaitingBgTask(Callable<?> callable)
{
return bgProcessor.getWaitingTasksCount(callable) > 0;
}
/**
* Return true if the specified runnable is waiting to be processed<br>
* in single scheme background processing (low priority).
*/
public static boolean hasWaitingBgSingleTask(Runnable runnable)
{
final InstanceProcessor processor = getBgInstanceProcessor(runnable);
return processor.hasWaitingTasks(runnable);
}
/**
* Return true if the specified callable is waiting to be processed<br>
* in single scheme background processing (low priority).
*/
public static boolean hasWaitingBgSingleTask(Callable<?> callable)
{
final InstanceProcessor processor = getBgInstanceProcessor(callable);
return processor.hasWaitingTasks(callable);
}
/**
* Return true if the specified runnable is waiting to be processed<br>
* in single scheme background processing (normal priority).
*/
public static boolean hasWaitingSingleTask(Runnable runnable)
{
final InstanceProcessor processor = getInstanceProcessor(runnable);
return processor.hasWaitingTasks(runnable);
}
/**
* Return true if the specified callable is waiting to be processed<br>
* in single scheme background processing (normal priority).
*/
public static boolean hasWaitingSingleTask(Callable<?> callable)
{
final InstanceProcessor processor = getInstanceProcessor(callable);
return processor.hasWaitingTasks(callable);
}
/**
* Return the number of active background tasks.
*/
public static int getActiveBgTaskCount()
{
return bgProcessor.getActiveCount();
}
/**
* Create a thread pool with the given name.<br>
* The number of processing thread is automatically calculated given the number of core of the
* system.
*
* @see Processor#Processor(int, int, int)
*/
public static ExecutorService createThreadPool(String name)
{
final Processor result = new Processor(SystemUtil.getNumberOfCPUs());
result.setThreadName(name);
return result;
}
/**
* Same as {@link Thread#sleep(long)} except Exception is caught and ignored.
*/
public static void sleep(long milli)
{
try
{
Thread.sleep(milli);
}
catch (InterruptedException e)
{
// have to interrupt the thread
Thread.currentThread().interrupt();
}
}
/**
* Same as {@link Thread#sleep(long)} except Exception is caught and ignored.
*/
public static void sleep(int milli)
{
try
{
Thread.sleep(milli);
}
catch (InterruptedException e)
{
// have to interrupt the thread
Thread.currentThread().interrupt();
}
}
}