/*
* 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.main.Icy;
import icy.system.IcyExceptionHandler;
import icy.system.SystemUtil;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Processor class.<br>
* Allow you to queue and execute tasks on a defined set of thread.
*
* @author stephane
*/
public class Processor extends ThreadPoolExecutor
{
public static final int DEFAULT_MAX_WAITING = 1024;
public static final int DEFAULT_MAX_PROCESSING = SystemUtil.getNumberOfCPUs();
/**
* @deprecated Useless interface
*/
@Deprecated
public interface ProcessorEventListener extends EventListener
{
public void processDone(Processor source, Runnable runnable);
}
protected class ProcessorThreadFactory implements ThreadFactory
{
String name;
public ProcessorThreadFactory(String name)
{
super();
setName(name);
}
public String getName()
{
return name;
}
public void setName(String value)
{
this.name = value;
}
String getThreadName()
{
String result = name;
return result;
}
@Override
public Thread newThread(Runnable r)
{
final Thread result = new Thread(r, getThreadName());
result.setPriority(priority);
return result;
}
}
protected class ProcessorRejectedExecutionHandler implements RejectedExecutionHandler
{
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor)
{
// ignore if we try to submit process while Icy is exiting
if (!Icy.isExiting())
throw new RejectedExecutionException("Cannot add new task, ignore execution of " + r);
}
}
protected class FutureTaskAdapter<T> extends FutureTask<T>
{
public Runnable runnable;
public Callable<T> callable;
final boolean handleException;
public FutureTaskAdapter(Runnable runnable, T result, boolean handleException)
{
super(runnable, result);
this.runnable = runnable;
this.callable = null;
this.handleException = handleException;
}
public FutureTaskAdapter(Runnable runnable, boolean handleException)
{
this(runnable, null, handleException);
}
public FutureTaskAdapter(Callable<T> callable, boolean handleException)
{
super(callable);
this.runnable = null;
this.callable = callable;
this.handleException = handleException;
}
@Override
protected void done()
{
super.done();
if (handleException)
{
try
{
get();
}
catch (Exception e)
{
IcyExceptionHandler.handleException(e.getCause(), true);
}
}
}
}
/**
* @deprecated
*/
@Deprecated
protected class RunnableAdapter implements Runnable
{
private final Runnable task;
private final boolean onEDT;
public RunnableAdapter(Runnable runnable, boolean onEDT)
{
super();
task = runnable;
this.onEDT = onEDT;
}
@Override
public void run()
{
if (task != null)
{
if (onEDT)
ThreadUtil.invokeNow(task);
else
task.run();
}
}
/**
* @return the task
*/
public Runnable getTask()
{
return task;
}
}
/**
* @deprecated
*/
@Deprecated
protected class CallableAdapter<T> implements Callable<T>
{
private final Callable<T> task;
private final boolean onEDT;
public CallableAdapter(Callable<T> task, boolean onEDT)
{
super();
this.task = task;
this.onEDT = onEDT;
}
/**
* @return the task
*/
public Callable<T> getTask()
{
return task;
}
@Override
public T call() throws Exception
{
if (task != null)
{
if (onEDT)
return ThreadUtil.invokeNow(task);
return task.call();
}
return null;
}
}
/**
* @deprecated
*/
@Deprecated
protected class FutureTaskAdapterEDT<T> extends FutureTaskAdapter<T>
{
public FutureTaskAdapterEDT(Runnable runnable, T result, boolean onEDT)
{
super(new RunnableAdapter(runnable, onEDT), result, true);
// assign the original runnable
this.runnable = runnable;
this.callable = null;
}
public FutureTaskAdapterEDT(Runnable runnable, boolean onEDT)
{
this(runnable, null, onEDT);
}
public FutureTaskAdapterEDT(Callable<T> callable, boolean onEDT)
{
super(new CallableAdapter<T>(callable, onEDT), true);
// assign the original callable
this.runnable = null;
this.callable = callable;
}
public Runnable getRunnable()
{
return runnable;
}
public Callable<T> getCallable()
{
return callable;
}
}
/**
* 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;
/**
* parameters
*/
int priority;
/**
* internal
*/
protected Runnable waitingExecution;
protected long lastAdd;
/**
* Create a new Processor with specified number of maximum waiting and processing tasks.<br>
*
* @param maxWaiting
* The length of waiting queue.
* @param numThread
* The maximum number of processing thread.
* @param priority
* Processor priority<br>
* <code>Processor.MIN_PRIORITY</code><br>
* <code>Processor.NORM_PRIORITY</code><br>
* <code>Processor.MAX_PRIORITY</code>
*/
public Processor(int maxWaiting, int numThread, int priority)
{
super(numThread, numThread, 2L, TimeUnit.SECONDS, (maxWaiting == -1) ? new LinkedBlockingQueue<Runnable>()
: new LinkedBlockingQueue<Runnable>(maxWaiting));
setThreadFactory(new ProcessorThreadFactory("Processor"));
setRejectedExecutionHandler(new ProcessorRejectedExecutionHandler());
allowCoreThreadTimeOut(true);
this.priority = priority;
waitingExecution = null;
}
/**
* Create a new Processor with specified number of maximum waiting and processing tasks.
*
* @param maxWaiting
* The length of waiting queue.
* @param numThread
* The maximum number of processing thread.
*/
public Processor(int maxWaiting, int numThread)
{
this(maxWaiting, numThread, NORM_PRIORITY);
}
/**
* Create a new Processor with specified number of processing thread.
*
* @param numThread
* The maximum number of processing thread.
*/
public Processor(int numThread)
{
this(-1, numThread, NORM_PRIORITY);
}
/**
* Create a new Processor with default number of maximum waiting and processing tasks.
*/
public Processor()
{
this(DEFAULT_MAX_WAITING, DEFAULT_MAX_PROCESSING);
}
/**
* @deprecated Use {@link #removeFirstWaitingTask(Runnable)} instead.
*/
@Deprecated
public boolean removeTask(Runnable task)
{
return removeFirstWaitingTask(task);
}
/**
* @deprecated Use {@link #submit(Runnable)} instead.
*/
@Deprecated
public boolean addTask(Runnable task, boolean onEDT, int id)
{
return addTask(task, onEDT);
}
/**
* @deprecated Use {@link #submit(Runnable)} instead.
*/
@Deprecated
public boolean addTask(Runnable task, boolean onEDT)
{
try
{
submit(task, onEDT);
}
catch (RejectedExecutionException E)
{
return false;
}
return true;
}
/**
* @deprecated Use {@link #submit(Runnable)} instead.
*/
@Deprecated
public boolean addTask(Runnable task)
{
return addTask(task, false);
}
@Override
public boolean remove(Runnable task)
{
// don't forget to remove the reference here
if (waitingExecution == task)
waitingExecution = null;
return super.remove(task);
}
/**
* @deprecated Use {@link #newTaskFor(Runnable, Object)} instead.
*/
@Deprecated
protected <T> FutureTaskAdapter<T> newTaskFor(Runnable runnable, T value, boolean onEDT)
{
return new FutureTaskAdapterEDT<T>(runnable, value, onEDT);
};
/**
* @deprecated Use {@link #newTaskFor(Callable)} instead.
*/
@Deprecated
protected <T> FutureTaskAdapter<T> newTaskFor(Callable<T> callable, boolean onEDT)
{
return new FutureTaskAdapterEDT<T>(callable, onEDT);
}
/**
* @param handledException
* if set to <code>true</code> then any occurring exception during the runnable
* processing will be catch by {@link IcyExceptionHandler}.
* @param runnable
* the runnable task being wrapped
* @param value
* the default value for the returned future
* @return a <tt>RunnableFuture</tt> which when run will run the underlying runnable and which,
* as a <tt>Future</tt>, will yield the given value as its result and provide for
* cancellation of the underlying task.
*/
protected <T> FutureTaskAdapter<T> newTaskFor(boolean handledException, Runnable runnable, T value)
{
return new FutureTaskAdapter<T>(runnable, value, handledException);
};
/**
* @param handledException
* if set to <code>true</code> then any occurring exception during the runnable
* processing will be catch by {@link IcyExceptionHandler}.
* @param callable
* the callable task being wrapped
* @return a <tt>RunnableFuture</tt> which when run will call the
* underlying callable and which, as a <tt>Future</tt>, will yield
* the callable's result as its result and provide for
* cancellation of the underlying task.
*/
protected <T> FutureTaskAdapter<T> newTaskFor(boolean handledException, Callable<T> callable)
{
return new FutureTaskAdapter<T>(callable, handledException);
}
@Override
public void execute(Runnable task)
{
super.execute(task);
// save the last executed task
waitingExecution = task;
}
/**
* Submit the given task (internal use only).
*/
protected synchronized <T> FutureTask<T> submit(FutureTaskAdapter<T> task)
{
execute(task);
return task;
}
@Override
public Future<?> submit(Runnable task)
{
if (task == null)
throw new NullPointerException();
return submit(newTaskFor(false, task, null));
}
@Override
public <T> Future<T> submit(Runnable task, T result)
{
if (task == null)
throw new NullPointerException();
return submit(newTaskFor(false, task, result));
}
@Override
public <T> Future<T> submit(Callable<T> task)
{
if (task == null)
throw new NullPointerException();
return submit(newTaskFor(false, task));
}
/**
* Submits a Runnable task for execution and returns a Future
* representing that task. The Future's <tt>get</tt> method will
* return <tt>null</tt> upon <em>successful</em> completion.
*
* @param handleException
* if set to <code>true</code> then any occurring exception during the runnable
* processing will be catch by {@link IcyExceptionHandler}.
* @param task
* the task to submit
* @return a Future representing pending completion of the task
* @throws RejectedExecutionException
* if the task cannot be
* scheduled for execution
* @throws NullPointerException
* if the task is null
*/
public Future<?> submit(boolean handleException, Runnable task)
{
if (task == null)
throw new NullPointerException();
return submit(newTaskFor(handleException, task, null));
}
/**
* Submits a Runnable task for execution and returns a Future
* representing that task. The Future's <tt>get</tt> method will
* return the given result upon successful completion.
*
* @param handleException
* if set to <code>true</code> then any occurring exception during the runnable
* processing will be catch by {@link IcyExceptionHandler}.
* @param task
* the task to submit
* @param result
* the result to return
* @return a Future representing pending completion of the task
* @throws RejectedExecutionException
* if the task cannot be
* scheduled for execution
* @throws NullPointerException
* if the task is null
*/
public <T> Future<T> submit(boolean handleException, Runnable task, T result)
{
if (task == null)
throw new NullPointerException();
return submit(newTaskFor(handleException, task, result));
}
/**
* Submits a value-returning task for execution and returns a
* Future representing the pending results of the task. The
* Future's <tt>get</tt> method will return the task's result upon
* successful completion.
* <p>
* If you would like to immediately block waiting for a task, you can use constructions of the form
* <tt>result = exec.submit(aCallable).get();</tt>
* <p>
* Note: The {@link Executors} class includes a set of methods that can convert some other common closure-like
* objects, for example, {@link java.security.PrivilegedAction} to {@link Callable} form so they can be submitted.
*
* @param handleException
* if set to <code>true</code> then any occurring exception during the runnable
* processing will be catch by {@link IcyExceptionHandler}.
* @param task
* the task to submit
* @return a Future representing pending completion of the task
* @throws RejectedExecutionException
* if the task cannot be
* scheduled for execution
* @throws NullPointerException
* if the task is null
*/
public <T> Future<T> submit(boolean handleException, Callable<T> task)
{
if (task == null)
throw new NullPointerException();
return submit(newTaskFor(handleException, task));
}
/**
* @deprecated Use {@link #submit(Runnable)} instead and ThreadUtil.invokeNow(..) where you need
* it.
*/
@Deprecated
public Future<?> submit(Runnable task, boolean onEDT)
{
if (task == null)
throw new NullPointerException();
return submit(newTaskFor(task, null, onEDT));
}
/**
* @deprecated Use {@link #submit(Runnable, Object)} instead and ThreadUtil.invokeNow(..) where
* you
* need it.
*/
@Deprecated
public <T> Future<T> submit(Runnable task, T result, boolean onEDT)
{
if (task == null)
throw new NullPointerException();
return submit(newTaskFor(task, result, onEDT));
}
/**
* @deprecated Use {@link #submit(Callable)} instead and ThreadUtil.invokeNow(..) where you need
* it.
*/
@Deprecated
public <T> Future<T> submit(Callable<T> task, boolean onEDT)
{
if (task == null)
throw new NullPointerException();
return submit(newTaskFor(task, onEDT));
}
/**
* Return true if one or more process are executing or we still have waiting tasks.
*/
public boolean isProcessing()
{
return (getActiveCount() > 0) || hasWaitingTasks();
}
/**
* Wait for all tasks completion
*/
public void waitAll()
{
while (isProcessing())
ThreadUtil.sleep(1);
}
/**
* shutdown and wait current tasks completion
*/
public void shutdownAndWait()
{
shutdown();
while (!isTerminated())
ThreadUtil.sleep(1);
}
/**
* @return the priority
*/
public int getPriority()
{
return priority;
}
/**
* @param priority
* the priority to set
*/
public void setPriority(int priority)
{
this.priority = priority;
}
/**
* @deprecated Use {@link #getThreadName()} instead
*/
@Deprecated
public String getDefaultThreadName()
{
return ((ProcessorThreadFactory) getThreadFactory()).getName();
}
/**
* @deprecated Use {@link #setThreadName(String)} instead
*/
@Deprecated
public void setDefaultThreadName(String defaultThreadName)
{
((ProcessorThreadFactory) getThreadFactory()).setName(defaultThreadName);
}
/**
* Return the thread name.
*/
public String getThreadName()
{
return ((ProcessorThreadFactory) getThreadFactory()).getName();
}
/**
* Set the wanted thread name.
*/
public void setThreadName(String defaultThreadName)
{
((ProcessorThreadFactory) getThreadFactory()).setName(defaultThreadName);
}
/**
* Get the number of free slot in queue
*/
public int getFreeSlotNumber()
{
return getQueue().remainingCapacity();
}
/**
* Return true if queue is full
*/
public boolean isFull()
{
return getFreeSlotNumber() == 0;
}
/**
* Return waiting tasks
*/
protected List<FutureTaskAdapter<?>> getWaitingTasks()
{
final BlockingQueue<Runnable> q = getQueue();
final List<FutureTaskAdapter<?>> result = new ArrayList<FutureTaskAdapter<?>>();
synchronized (q)
{
for (Runnable r : q)
result.add((FutureTaskAdapter<?>) r);
}
return result;
}
/**
* Return waiting tasks for the specified Runnable instance
*/
protected List<FutureTaskAdapter<?>> getWaitingTasks(Runnable task)
{
final List<FutureTaskAdapter<?>> result = new ArrayList<FutureTaskAdapter<?>>();
// scan all tasks
for (FutureTaskAdapter<?> f : getWaitingTasks())
if (f.runnable == task)
result.add(f);
return result;
}
/**
* Return waiting tasks for the specified Callable instance
*/
protected List<FutureTaskAdapter<?>> getWaitingTasks(Callable<?> task)
{
final List<FutureTaskAdapter<?>> result = new ArrayList<FutureTaskAdapter<?>>();
// scan all tasks
for (FutureTaskAdapter<?> f : getWaitingTasks())
if (f.callable == task)
result.add(f);
return result;
}
/**
* Return the number of waiting task
*/
public int getWaitingTasksCount()
{
final int result = getQueue().size();
// TODO : be sure that waitingExecution pass to null when task has been taken in account.
// Queue can be empty right after a task submission.
// For this particular case we return 1 if a task has been submitted
// and not taken in account with a timeout of 1 second.
if ((result == 0) && ((waitingExecution != null) && ((System.currentTimeMillis() - lastAdd) < 1000)))
return 1;
return result;
}
/**
* @deprecated Not anymore supported.<br>
* Use {@link #getWaitingTasksCount(Callable)} or {@link #getWaitingTasksCount(Runnable)} instead.
*/
@Deprecated
public int getWaitingTasksCount(int id)
{
return 0;
}
/**
* Return the number of task waiting in queue for the specified <tt>Runnable</tt> instance.
*/
public int getWaitingTasksCount(Runnable task)
{
int result = 0;
for (FutureTaskAdapter<?> f : getWaitingTasks())
if (f.runnable == task)
result++;
return result;
}
/**
* Return the number of task waiting in queue for the specified <tt>Callable</tt> instance.
*/
public int getWaitingTasksCount(Callable<?> task)
{
int result = 0;
for (FutureTaskAdapter<?> f : getWaitingTasks())
if (f.callable == task)
result++;
return result;
}
/**
* Return true if we have at least one task waiting in queue
*/
public boolean hasWaitingTasks()
{
return (getWaitingTasksCount() > 0);
}
/**
* @deprecated Not anymore supported.<br>
* Use {@link #hasWaitingTasks(Callable)} or {@link #hasWaitingTasks(Runnable)} instead.
*/
@Deprecated
public boolean hasWaitingTasks(int id)
{
return false;
}
/**
* Return true if we have at least one task in queue for the specified <tt>Runnable</tt> instance.
*/
public boolean hasWaitingTasks(Runnable task)
{
// scan all tasks
for (FutureTaskAdapter<?> f : getWaitingTasks())
if (f.runnable == task)
return true;
return false;
}
/**
* Return true if we have at least one task in queue for the specified <tt>Callable</tt> instance.
*/
public boolean hasWaitingTasks(Callable<?> task)
{
// scan all tasks
for (FutureTaskAdapter<?> f : getWaitingTasks())
if (f.callable == task)
return true;
return false;
}
/**
* @deprecated Not anymore supported.<br>
* USe {@link #removeFirstWaitingTask(Runnable)} or {@link #removeFirstWaitingTask(Callable)} instead.
*/
@Deprecated
public boolean removeFirstWaitingTask(int id)
{
return false;
}
/**
* Remove first waiting task for the specified <tt>FutureTaskAdapter</tt> instance.
*/
protected boolean removeFirstWaitingTask(FutureTaskAdapter<?> task)
{
if (task == null)
return false;
synchronized (getQueue())
{
// remove first task of specified instance
for (FutureTaskAdapter<?> f : getWaitingTasks())
if (f == task)
return remove(f);
}
return false;
}
/**
* Remove first waiting task for the specified <tt>Runnable</tt> instance.
*/
public boolean removeFirstWaitingTask(Runnable task)
{
if (task == null)
return false;
synchronized (getQueue())
{
// remove first task of specified instance
for (FutureTaskAdapter<?> f : getWaitingTasks())
if (f.runnable == task)
return remove(f);
}
return false;
}
/**
* Remove first waiting task for the specified <tt>Callable</tt> instance.
*/
public boolean removeFirstWaitingTask(Callable<?> task)
{
if (task == null)
return false;
synchronized (getQueue())
{
// remove first task of specified instance
for (FutureTaskAdapter<?> f : getWaitingTasks())
if (f.callable == task)
return remove(f);
}
return false;
}
/**
* @deprecated Not anymore supported.<br>
* USe {@link #removeWaitingTasks(Runnable)} or {@link #removeWaitingTasks(Callable)} instead.
*/
@Deprecated
public boolean removeWaitingTasks(int id)
{
return false;
}
/**
* Remove all waiting tasks for the specified <tt>Runnable</tt> instance.
*/
public boolean removeWaitingTasks(Runnable task)
{
boolean result = false;
synchronized (getQueue())
{
// remove all tasks of specified instance
for (FutureTaskAdapter<?> f : getWaitingTasks(task))
result |= remove(f);
}
return result;
}
/**
* Remove all waiting tasks for the specified <tt>Callable</tt> instance.
*/
public boolean removeWaitingTasks(Callable<?> task)
{
boolean result = false;
synchronized (getQueue())
{
// remove all tasks of specified instance
for (FutureTaskAdapter<?> f : getWaitingTasks(task))
result |= remove(f);
}
return result;
}
/**
* Clear all waiting tasks
*/
public void removeAllWaitingTasks()
{
waitingExecution = null;
synchronized (getQueue())
{
// remove all tasks
getQueue().clear();
}
}
/**
* @deprecated This method is useless.
*/
@Deprecated
public void limitWaitingTask(Runnable task, int value)
{
synchronized (getQueue())
{
final List<FutureTaskAdapter<?>> tasks = getWaitingTasks(task);
final int numToRemove = tasks.size() - value;
for (int i = 0; i < numToRemove; i++)
remove(tasks.get(i));
}
}
/**
* @deprecated Not anymore supported !
*/
@Deprecated
public void limitWaitingTask(int id, int value)
{
// not anymore supported
}
/**
* @deprecated This method is useless.
*/
@Deprecated
public boolean limitWaitingTask(int value)
{
synchronized (getQueue())
{
final List<FutureTaskAdapter<?>> tasks = getWaitingTasks();
final int numToRemove = tasks.size() - value;
for (int i = 0; i < numToRemove; i++)
remove(tasks.get(i));
}
return false;
}
/**
* @deprecated Useless...
*/
@Deprecated
public void addListener(ProcessorEventListener listener)
{
//
}
/**
* @deprecated Useless...
*/
@Deprecated
public void removeListener(ProcessorEventListener listener)
{
//
}
/**
* @deprecated useless
*/
@Deprecated
public void fireDoneEvent(FutureTaskAdapter<?> task)
{
//
}
@Override
protected void beforeExecute(Thread t, Runnable r)
{
super.beforeExecute(t, r);
// ok we can remove reference...
waitingExecution = null;
}
}