package jadex.commons.service.execution; import jadex.commons.Future; import jadex.commons.ICommand; import jadex.commons.IFuture; import jadex.commons.collection.SCollection; import jadex.commons.concurrent.CounterResultListener; import jadex.commons.concurrent.DelegationResultListener; import jadex.commons.concurrent.Executor; import jadex.commons.concurrent.IExecutable; import jadex.commons.concurrent.IResultListener; import jadex.commons.service.BasicService; import jadex.commons.service.IServiceProvider; import jadex.commons.service.SServiceProvider; import jadex.commons.service.threadpool.IThreadPoolService; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * The asynchronous executor service that executes all tasks in separate executors. */ public class AsyncExecutionService extends BasicService implements IExecutionService { // //-------- static part -------- // // public static MultiCollection DEBUG = new MultiCollection(); //-------- attributes -------- /** The threadpool. */ protected IThreadPoolService threadpool; /** The currently waiting tasks (task->executor). */ protected Map executors; /** The idle commands. */ protected Set idlecommands; /** The state. */ protected boolean running; /** The shutdown flag. */ protected boolean shutdown; /** The provider. */ protected IServiceProvider provider; /** The executor cache. */ // protected List executorcache; /** The maximum number of cached executors. */ // protected int max; //-------- constructors -------- /** * Create a new asynchronous executor service. * / public AsyncExecutorService(IThreadPool threadpool) { this(threadpool, 100); }*/ /** * Create a new asynchronous executor service. */ public AsyncExecutionService(IServiceProvider provider)//, int max) { this(provider, null); } /** * Create a new asynchronous executor service. */ public AsyncExecutionService(IServiceProvider provider, Map properties)//, int max) { super(provider.getId(), IExecutionService.class, properties); this.provider = provider; // this.threadpool = threadpool; // this.max = max; this.running = false; this.executors = SCollection.createHashMap(); // this.executors = SCollection.createWeakHashMap(); // this.executorcache = SCollection.createArrayList(); } //-------- methods -------- /** * Execute a task in its own thread. * @param task The task to execute. * (called from arbitrary threads) */ public synchronized void execute(final IExecutable task) { // synchronized(DEBUG) // { // DEBUG.put(task, "execute called"); // } //System.out.println("execute called: "+task); if(!isValid()) throw new RuntimeException("Not running: "+task); if(shutdown) throw new RuntimeException("Shutting down: "+task); Executor exe = (Executor)executors.get(task); if(exe==null) { // synchronized(DEBUG) // { // DEBUG.put(task, "creating executor"); // } // System.out.println("Created executor for:"+task); // if(executorcache.size()>0) // { // exe = (Executor)executorcache.remove(0); // exe.setExecutable(task); // } // else // { exe = new Executor(threadpool, task) { // Hack!!! overwritten for debugging. protected boolean code() { // synchronized(DEBUG) // { // DEBUG.put(task, "executing code(): "+this); // } boolean ret = super.code(); // synchronized(DEBUG) // { // DEBUG.put(task, "code() finished: "+this); // } return ret; } // Hack!!! overwritten to know, when executor ends. public void run() { super.run(); ICommand[] commands = null; synchronized(AsyncExecutionService.this) { synchronized(this) { // isRunning() refers to running state of executor! // Do not remove when a new executor has already been added for the task. if(!this.isRunning() && executors!=null && executors.get(task)==this) { // System.out.println("Removing executor for: "+task+", "+this); executors.remove(task); // weak for testing // setExecutable(null); // if(executorcache.size()<max) // executorcache.add(this); // When no more executables, inform idle commands. if(AsyncExecutionService.this.running && idlecommands!=null && executors.isEmpty()) { // System.out.println("idle"); commands = (ICommand[])idlecommands.toArray(new ICommand[idlecommands.size()]); } // else if(AsyncExecutionService.this.running && idlecommands!=null) // { // System.out.println("not idle: "+executors); // } } } } for(int i=0; commands!=null && i<commands.length; i++) { commands[i].execute(null); } } }; // } // synchronized(DEBUG) // { // DEBUG.put(task, "new executor: "+exe); // } executors.put(task, exe); } if(running) { // synchronized(DEBUG) // { // DEBUG.put(task, "calling execute(): "+exe); // } // System.out.println("Executing for: "+task+", "+exe); exe.execute(); } } /** * Cancel a task. Triggers the task to * be not executed in future. * @param task The task to execute. * @param listener The listener. */ public synchronized IFuture cancel(final IExecutable task) { // todo: repair me: problem is that method can interfere with execute?! final Future ret = new Future(); if(!isValid()) { ret.setException(new RuntimeException("Shutting down.")); } else { final Executor exe = (Executor)executors.get(task); if(exe!=null) { IResultListener lis = new IResultListener() { public void resultAvailable(Object source, Object result) { // todo: do not call listener with holding lock synchronized(AsyncExecutionService.this) { ret.setResult(result); if(executors!=null) { executors.remove(task); // System.out.println("Removing executor with lis for: "+task+", "+exe); } } } public void exceptionOccurred(Object source, Exception exception) { // todo: do not call future with holding lock synchronized(AsyncExecutionService.this) { ret.setResult(exception); if(executors!=null) { executors.remove(task); // System.out.println("Removing executor with lis e for: "+task+", "+exe); } } } }; exe.shutdown().addResultListener(lis); // executors.remove(task); } else { ret.setResult(null); } } return ret; } /** * Get the currently running or waiting tasks. */ public synchronized IExecutable[] getTasks() { return (IExecutable[])executors.keySet().toArray(new IExecutable[executors.size()]); } /** * Start the execution service. * Resumes all scheduled tasks. */ public synchronized IFuture startService() { final Future ret = new Future(); if(shutdown) { ret.setException(new RuntimeException("Cannot start: shutdowning service.")); } else { SServiceProvider.getService(provider, IThreadPoolService.class).addResultListener(new IResultListener() { public void resultAvailable(Object source, Object result) { try { threadpool = (IThreadPoolService)result; running = true; // new RuntimeException("AsyncExecustionService.start()").printStackTrace(); if(!executors.isEmpty()) { // Resume all suspended tasks. IExecutable[] keys = (IExecutable[])executors.keySet() .toArray(new IExecutable[executors.size()]); for(int i=0; i<keys.length; i++) execute(keys[i]); } else if(idlecommands!=null) { // System.out.println("restart: idle"); Iterator it = idlecommands.iterator(); while(it.hasNext()) { ((ICommand)it.next()).execute(null); } } ret.setResult(null); } catch(RuntimeException e) { ret.setException(e); } } public void exceptionOccurred(Object source, Exception exception) { ret.setException(exception); } }); } return ret; } /** * Shutdown the executor service. * // todo: make callable more than once */ public synchronized IFuture shutdownService() { final Future ret = new Future(); if(shutdown) { ret.setException((new RuntimeException("Already shutdowned."))); } else { shutdown = true; IExecutable[] keys = (IExecutable[])executors.keySet() .toArray(new IExecutable[executors.size()]); if(keys.length>0) { // One listener counts until all executors have shutdowned. IResultListener lis = new CounterResultListener(keys.length, new DelegationResultListener(ret)); for(int i=0; i<keys.length; i++) { Executor exe = (Executor)executors.get(keys[i]); if(exe!=null) exe.shutdown().addResultListener(lis); } } else { ret.setResult(null); } } executors = null; return ret; } /** * Test if the service is valid. * @return True, if service can be used. */ public synchronized boolean isValid() { return running && !shutdown; } /** * Test if the executor is currently idle. */ public synchronized boolean isIdle() { return running && !executors.isEmpty(); } /** * Add a command to be executed whenever the executor * is idle (i.e. no executables running). */ public void addIdleCommand(ICommand command) { if(idlecommands==null) { synchronized(this) { if(idlecommands==null) { idlecommands = SCollection.createLinkedHashSet(); } } } idlecommands.add(command); } /** * Remove a previously added idle command. */ public synchronized void removeIdleCommand(ICommand command) { if(idlecommands!=null) idlecommands.remove(command); } }