/* * Eoulsan development code * * This code may be freely distributed and modified under the * terms of the GNU Lesser General Public License version 2.1 or * later and CeCILL-C. This should be distributed with the code. * If you do not have a copy, see: * * http://www.gnu.org/licenses/lgpl-2.1.txt * http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.txt * * Copyright for this code is held jointly by the Genomic platform * of the Institut de Biologie de l'École normale supérieure and * the individual authors. These should be listed in @author doc * comments. * * For more information on the Eoulsan project and its aims, * or to join the Eoulsan Google group, visit the home page * at: * * http://outils.genomique.biologie.ens.fr/eoulsan * */ package fr.ens.biologie.genomique.eoulsan.core.schedulers; import static com.google.common.base.Preconditions.checkArgument; import static fr.ens.biologie.genomique.eoulsan.EoulsanLogger.getLogger; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import fr.ens.biologie.genomique.eoulsan.EoulsanRuntimeException; import fr.ens.biologie.genomique.eoulsan.core.Step; import fr.ens.biologie.genomique.eoulsan.core.workflow.TaskContextImpl; import fr.ens.biologie.genomique.eoulsan.core.workflow.TaskResultImpl; /** * This class define a muti thread scheduler. * @author Laurent Jourdren * @since 2.0 */ public class MultiThreadTaskScheduler extends AbstractTaskScheduler implements Runnable { private static final int SLEEP_TIME_IN_MS = 500; private static final int WAIT_SHUTDOWN_MINUTES = 60; private final PausableThreadPoolExecutor executor; private final Set<Future<TaskThread>> threads = new HashSet<>(); /** * Wrapper class around a call to executeTask methods. * @author Laurent Jourdren */ private final class TaskThread implements Runnable { private final TaskContextImpl context; private final long submissionTime; private Throwable e; private boolean done; @Override public void run() { try { // Do nothing if scheduler is stopped if (isStopped()) { this.done = true; return; } // Set task in running state beforeExecuteTask(this.context); // Execute the context final TaskResultImpl result = executeTask(this.context); // Do nothing if scheduler is stopped if (isStopped()) { this.done = true; return; } // Set task in done state afterExecuteTask(this.context, result); this.done = true; } catch (Throwable e) { this.e = e; } } public void fail(final boolean cancel) { final long endTime = System.currentTimeMillis(); final Throwable exception = this.e != null ? this.e : new EoulsanRuntimeException("Task #" + context.getId() + "has failed without exception, cancel=" + cancel); final TaskResultImpl result = new TaskResultImpl(this.context, new Date(this.submissionTime), new Date(endTime), endTime - this.submissionTime, exception, exception.getMessage()); afterExecuteTask(this.context, result); } // // Constructor // /** * Constructor. * @param context context to execute */ TaskThread(final TaskContextImpl context) { this.context = context; this.submissionTime = System.currentTimeMillis(); } } @Override public void submit(final Step step, final TaskContextImpl context) { // Call to the super method super.submit(step, context); // Create context thread final TaskThread st = new TaskThread(context); // Submit the context thread the thread executor synchronized (this.threads) { this.threads.add(this.executor.submit(st, st)); } } @Override public void start() { // Call to the super method super.start(); new Thread(this, "TaskScheduler_multi_thread").start(); } @Override public void stop() { // Call to the super method super.stop(); try { // Shutdown the executor this.executor.shutdownNow(); // Wait the termination of current running task this.executor.awaitTermination(WAIT_SHUTDOWN_MINUTES, TimeUnit.MINUTES); } catch (InterruptedException e) { getLogger().severe(e.getMessage()); } } @Override public void pause() { // Call to the super method super.pause(); this.executor.pause(); } @Override public void resume() { // Call to the super method super.resume(); this.executor.resume(); } // // Runnable method // @Override public void run() { // The list of finished tasks final List<Future<TaskThread>> threadsToRemove = new ArrayList<>(); while (!this.isStopped()) { for (Future<TaskThread> ftt : this.threads) { // For all finished tasks if (ftt.isDone()) { try { final TaskThread tt = ftt.get(); // Check if the task has been correctly executed if (!tt.done) { tt.fail(ftt.isCancelled()); } // Add the task to the list of task to remove threadsToRemove.add(ftt); } catch (InterruptedException | ExecutionException e) { getLogger().severe("Unexcepted exception in " + this.getClass().getSimpleName() + ".run(): " + e.getMessage()); } } } // Remove the finished tasks from the list of tasks if (!this.threads.isEmpty()) { synchronized (threadsToRemove) { for (Future<TaskThread> ftt : threadsToRemove) { this.threads.remove(ftt); } } threadsToRemove.clear(); } // Wait try { Thread.sleep(SLEEP_TIME_IN_MS); } catch (InterruptedException e) { getLogger().severe(e.getMessage()); } } } // // Constructor // /** * Constructor. * @param threadNumber number of thread to use by the task scheduler */ public MultiThreadTaskScheduler(final int threadNumber) { checkArgument(threadNumber > 0, "threadNumber must be > 0"); // Create executor service this.executor = new PausableThreadPoolExecutor(threadNumber); } }