package jadex.commons.concurrent;
import jadex.commons.Future;
import jadex.commons.IFuture;
import java.util.ArrayList;
import java.util.List;
/**
* A helper class for running a single instance
* of code using the thread pool.
* The code to be executed has to be placed in the
* code() method.
* Once created, the execute() method may be called
* as often as desired. When no thread is currently
* executing the object, a new thread is used from the
* thread pool. Otherwise, the already existing thread
* continues execution.
* After shutdown() is called, the executor stops
* execution, even when execute() is called afterwards.
*/
public class Executor implements Runnable
{
//-------- attributes --------
/** Flag indicating if the thread is running. */
protected boolean running;
/** Flag indicating if the thread wants to run. */
protected boolean wanttorun;
/** Flag indicating that the executor shuts down. */
protected boolean shutdown;
/** Flag indicating that the executor has shutdowned. */
protected boolean shutdowned;
/** The thread pool. */
protected IThreadPool threadpool;
/** The executable. */
protected IExecutable executable;
/** The shutdown futures. */
protected List shutdownfutures;
//--------- constructors --------
/**
* Create an executor object.
* Constructor for subclasses overriding
* the code() method.
*/
public Executor(IThreadPool threadpool)
{
this(threadpool, null);
}
/**
* Create an executor object.
*/
public Executor(IThreadPool threadpool, IExecutable executable)
{
if(threadpool==null)
throw new IllegalArgumentException("Threadpool must not null.");
this.threadpool = threadpool;
this.executable = executable;
this.shutdownfutures = new ArrayList();
}
//-------- methods --------
/**
* Execute the code.
*/
public void run()
{
// running is already set to true in execute()
boolean iwanttorun = true;
while(iwanttorun && !shutdown)
{
iwanttorun = code();
// Setting flags in synchronized block assures,
// that execute is not called in between.
// Separating running and myrunning allows that this thread
// may terminate (myrunning==false) while a new thread
// is already starting (running==true).
synchronized(this)
{
//if(iwanttorun) System.out.println("continuing: "+this);
//else if(wanttorun) System.out.println("forced to continue: "+this);
iwanttorun = iwanttorun || wanttorun;
running = iwanttorun;
wanttorun = false; // reset until execute() is called again.
}
// try
// {
// Thread.sleep(10);
// }
// catch(InterruptedException e)
// {
// throw new RuntimeException(e);
// }
}
// Notify shutdown listeners when execution has ended.
Future[] futures = null;
synchronized(this)
{
if(shutdown)
{
futures = (Future[])shutdownfutures.toArray(new Future[shutdownfutures.size()]);
shutdownfutures.clear();
}
}
if(futures!=null)
{
for(int i=0; i<futures.length; i++)
futures[i].setResult(null);
synchronized(this)
{
shutdowned = true;
}
}
//System.out.println("exited: "+this);
}
/**
* Make sure a thread is executing the code.
*/
public void execute()
{
boolean execute = false;
synchronized(this)
{
// System.out.println("executing: "+this+" "+running);
// Thread.dumpStack();
if(!shutdown)
{
// Indicate that thread should continue to run (if running).
wanttorun = true;
if(!running)
{
running = true;
// Invoke the code of the executor object using the thread pool,
// which allows thread to be shared, when code is idle.
execute = true;
}
}
}
if(execute)
threadpool.execute(this);
}
/**
* Shutdown the executor.
*/
public IFuture shutdown()
{
Future ret = new Future();
boolean directnotify = false;
synchronized(this)
{
shutdown = true;
if(!shutdowned)
{
shutdownfutures.add(ret);
}
else
{
directnotify = true;
}
}
if(directnotify)
ret.setResult(null);
return ret;
}
/**
* Set the executable.
* @param executable The executable.
*/
public void setExecutable(IExecutable executable)
{
// if(this.executable!=null)
// throw new RuntimeException("Setting executable allowed only once.");
this.executable = executable;
}
/**
* The code to be run.
* @return True, when execution should continue.
*/
protected boolean code()
{
return executable.execute();
}
/**
* Check if the executor is running.
* Should only be called, when access to executor is
* correctly synchronized, otherwise inconsistent values might be returned.
*/
protected boolean isRunning()
{
return running;
}
/**
* Return true, if already shutowned.
* @return True, if shutdowned.
* /
protected boolean isShutdowned()
{
return shutdowned;
}*/
/**
* Create a string representation of this executor.
* /
public String toString()
{
return "Executor("+(executable!=null?executable.toString():super.toString())+")";
}*/
}