/******************************************************************************* * GenPlay, Einstein Genome Analyzer * Copyright (C) 2009, 2014 Albert Einstein College of Medicine * * This program 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. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. * Authors: Julien Lajugie <julien.lajugie@einstein.yu.edu> * Nicolas Fourel <nicolas.fourel@einstein.yu.edu> * Eric Bouhassira <eric.bouhassira@einstein.yu.edu> * * Website: <http://genplay.einstein.yu.edu> ******************************************************************************/ package edu.yu.einstein.genplay.core.operationPool; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import edu.yu.einstein.genplay.core.manager.project.ProjectChromosomes; import edu.yu.einstein.genplay.core.manager.project.ProjectManager; import edu.yu.einstein.genplay.gui.event.operationProgressEvent.OperationProgressEvent; import edu.yu.einstein.genplay.gui.event.operationProgressEvent.OperationProgressEventsGenerator; import edu.yu.einstein.genplay.gui.event.operationProgressEvent.OperationProgressListener; /** * Pool of threads with tools to start, interrupt and retrieve the result of the execution. * Generate progress events and send this events to listeners. * @author Julien Lajugie */ public final class OperationPool implements OperationProgressEventsGenerator { private static OperationPool instance = null; // unique instance of this singleton class /** * @return an instance of the singleton class {@link OperationPool} */ public static OperationPool getInstance() { if (instance == null) { instance = new OperationPool(ProjectManager.getInstance().getProjectChromosomes()); } return instance; } private ExecutorService executor; // thread executor private final List<OperationProgressListener> progressListeners; // list of progress listeners /** * Private constructor of the singleton class {@link OperationPool} * @param projectChromosomes a {@link ProjectChromosomes} */ private OperationPool(ProjectChromosomes projectChromosomes) { super(); progressListeners = new ArrayList<OperationProgressListener>(); initExecutorService(); } @Override public void addOperationProgressListener(OperationProgressListener operationProgressListener) { progressListeners.add(operationProgressListener); } @Override public OperationProgressListener[] getOperationProgressListeners() { OperationProgressListener[] listeners = new OperationProgressListener[progressListeners.size()]; return progressListeners.toArray(listeners); } /** * Creates a thread pool containing one thread per processor */ private void initExecutorService() { int nbProcessor = Runtime.getRuntime().availableProcessors(); executor = Executors.newFixedThreadPool(nbProcessor); } /** * Notifies the executor that a thread is done */ public synchronized void notifyDone() { notifyAll(); } /** * Notifies all the listeners that the progression of an operation changed * @param progressState state of the progression * @param completion completion if the state is IN_PROGRESS */ private void notifyProgressListeners(int progressState, double completion) { OperationProgressEvent evt = new OperationProgressEvent(progressState, completion); for (OperationProgressListener listener: progressListeners) { listener.operationProgressChanged(evt); } } @Override public void removeOperationProgressListener(OperationProgressListener operationProgressListener) { progressListeners.remove(operationProgressListener); } /** * Starts the pool of thread. Waits until the end of the execution and returns the result in a list. * An InterruptedException is thrown if the execution is stopped before the end. * @param <T> type returned by the threads * @param threads a list of {@link Callable} * @return a list of the specified type * @throws InterruptedException * @throws ExecutionException */ public synchronized <T> List<T> startPool(Collection<? extends Callable<T>> threads) throws InterruptedException, ExecutionException { ProjectChromosomes projectChromosomes = ProjectManager.getInstance().getProjectChromosomes(); // notify the listeners that the operation starts notifyProgressListeners(OperationProgressEvent.STARTING, 0d); // list of futur for the result of the callables List<Future<T>> futures = new ArrayList<Future<T>>(); // list for the return value of this method List<T> results = new ArrayList<T>(); for (Callable<T> currentCallable: threads) { futures.add(executor.submit(currentCallable)); } boolean stillAlive = true; while (stillAlive) { wait(1000); // if the executor is terminated or shut down if (executor.isTerminated() || executor.isShutdown()) { // we cancel all the futures if there not done for (short i = 0; i < futures.size(); i++) { if (!futures.get(i).isDone()) { futures.get(i).cancel(true); } } // we notify the listeners notifyProgressListeners(OperationProgressEvent.ABORT, 100d); throw new InterruptedException(); } long done = 0; stillAlive = false; // compute the completion and check if everything's done for (short i = 0; i < futures.size(); i++) { if (futures.get(i).isDone() || futures.get(i).isCancelled()) { done += projectChromosomes.get(i).getLength(); } else { stillAlive = true; } } long genomeLength = projectChromosomes.getGenomeLength(); double completion = 0; if (genomeLength != 0) { completion = (done / (double) genomeLength) * 100d; } int progressState = OperationProgressEvent.IN_PROGRESS; notifyProgressListeners(progressState, completion); } // generate the result list from the future list for (int j = 0; j < futures.size(); j++) { results.add(futures.get(j).get()); } // notify the listeners that the operation is complete notifyProgressListeners(OperationProgressEvent.COMPLETE, 100d); return results; } /** * Interrupts all the running thread and cancel the execution */ public synchronized void stopPool() { if ((executor != null) && (!executor.isShutdown()) && (!executor.isTerminated())) { executor.shutdownNow(); notifyAll(); initExecutorService(); } } }