/*
Copyright 2014 Julia s.r.l.
This file is part of BeeDeeDee.
BeeDeeDee 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.
BeeDeeDee 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 BeeDeeDee. If not, see <http://www.gnu.org/licenses/>.
*/
package com.juliasoft.utils.concurrent;
import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import static com.juliasoft.julia.checkers.nullness.assertions.NullnessAssertions.*;
/**
* An utility class to run tasks in parallel or asynchronously.
*
* @author <A HREF="mailto:fausto.spoto@univr.it">Fausto Spoto</A>
*/
public abstract class Executors {
/**
* A thread-pool of executors.
*/
private final static ExecutorService exec = java.util.concurrent.Executors.newCachedThreadPool();
/**
* The number of cores available on the run-time system.
*/
private final static int cpus = Runtime.getRuntime().availableProcessors();
/**
* Submits the given task as an asynchronous task.
*
* @param <T> the type of the result of the task
* @param task the task
* @return the future of the task
*/
public static <T> Task<T> submit(Callable<T> task) {
assertNonNull(task);
return new Task<T>(exec.submit(new WrapperCallable<T>(task)));
}
/**
* A wrapper of a callable. This allows the garbage collector to claim back
* all resources allocated for the wrapped callable.
*
* @author <A HREF="mailto:fausto.spoto@univr.it">Fausto Spoto</A>
*
* @param <T> the type of the result of the callable
*/
private static class WrapperCallable<T> implements Callable<T> {
/**
* The wrapped callable.
*/
private Callable<T> callable;
/**
* Builds the wrapper.
*
* @param callable the wrapped callable
*/
private WrapperCallable(Callable<T> callable) {
this.callable = callable;
}
@Override
public T call() throws Exception {
Callable<T> callable = this.callable;
if (callable != null) {
T result = callable.call();
// we unlink the callable and everything that might be reachable from it
this.callable = null;
return result;
}
return null;
}
}
/**
* Runs as many instances of the given {@code task} as there
* are available processors on this system. It blocks until all
* instances have finished.
*
* @param task the task
*/
public static void parallelise(Runnable task) {
Future<?>[] future = new Future<?>[cpus];
for (int i = 0; i < cpus; i++)
future[i] = exec.submit(task);
for (int i = 0; i < cpus; i++)
try {
future[i].get();
}
// we transform any exception in a runtime exception
// so that we do not have to express a throws clause
// in a lot of methods
catch (InterruptedException e) {
e.printStackTrace();
Throwable cause = e.getCause();
throw cause instanceof RuntimeException ? (RuntimeException) cause : new RuntimeException(cause);
}
catch (ExecutionException e) {
e.printStackTrace();
throw new RuntimeException(e.getCause());
}
}
/**
* Runs the given tasks in parallel. It blocks until they have all completed.
*
* @param tasks the tasks
*/
public static void parallelise(Runnable... tasks) {
assertNonNull(tasks);
Future<?>[] future = new Future<?>[tasks.length];
for (int i = 0; i < future.length; i++)
future[i] = exec.submit(tasks[i]);
for (int i = 0; i < future.length; i++)
try {
future[i].get();
}
// we transform any exception in a runtime exception
// so that we do not have to express a throws clause
// in a lot of methods
catch (InterruptedException e) {
e.printStackTrace();
Throwable cause = e.getCause();
throw cause instanceof RuntimeException ? (RuntimeException) cause : new RuntimeException(cause);
}
catch (ExecutionException e) {
e.printStackTrace();
throw new RuntimeException(e.getCause());
}
}
/**
* Runs the given tasks in parallel. It blocks until they have all completed.
*
* @param tasks the tasks
*/
public static void parallelise(Collection<? extends Runnable> tasks) {
assertNonNull(tasks);
Future<?>[] futures = new Future<?>[tasks.size()];
int i = 0;
for (Runnable task: tasks)
futures[i++] = exec.submit(task);
for (Future<?> future: futures)
try {
future.get();
}
// we transform any exception in a runtime exception
// so that we do not have to express a throws clause
// in a lot of methods
catch (InterruptedException e) {
e.printStackTrace();
Throwable cause = e.getCause();
throw cause instanceof RuntimeException ? (RuntimeException) cause : new RuntimeException(cause);
}
catch (ExecutionException e) {
e.printStackTrace();
throw new RuntimeException(e.getCause());
}
}
/**
* Creates with the given factory and runs as many tasks as there
* are available processors on this system. It blocks until all
* instances have finished.
*
* @param factory the factory
*/
public static void parallelise(RunnableFactory factory) {
assertNonNull(factory);
Runnable[] tasks = new Runnable[cpus];
for (int i = 0; i < cpus; i++)
tasks[i] = factory.mk();
parallelise(tasks);
}
/**
* Shutdowns the executors.
*/
public static void shutdown() {
exec.shutdown();
}
}