package ini.trakem2.parallel;
import ini.trakem2.utils.Utils;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
/**
* Like clojure's pmap, given a sequence of inputs obtained from an {@link Iterable},
* this {@link Iterable} will apply a function to each input ahead of consumption
* in a pool of threads managed by an {@link ExecutorService}.
*
* Does not hold onto the head of the input sequence.
* The sequence can be consumed only once, i.e. only a single call to {@link #iterator()} is possible.
*
* This class ought to be an {@link Iterator} rather than an {@link Iterable},
* but it is an {@link Iterable} for convenience, so that the for loop construct works.
*/
public final class ParallelMapping<I,O> implements Iterable<O>
{
final private Iterator<I> in;
final private int n_proc;
final private TaskFactory<I, O> generator;
public ParallelMapping(final Iterable<I> inputs, final TaskFactory<I,O> generator) {
this(Runtime.getRuntime().availableProcessors(), inputs.iterator(), generator);
}
public ParallelMapping(final Iterator<I> inputs, final TaskFactory<I,O> generator) {
this(Runtime.getRuntime().availableProcessors(), inputs, generator);
}
public ParallelMapping(final int n_proc, final Iterable<I> inputs, final TaskFactory<I,O> generator) {
this(n_proc, inputs.iterator(), generator);
}
/**
*
* @param n_proc Number of threads.
* @param inputs The sequence of inputs.
* @param generator The generator of {@link Callable} functions, one per input, each returning one output.
*/
public ParallelMapping(final int n_proc, final Iterator<I> inputs, final TaskFactory<I,O> generator) {
this.in = inputs;
this.n_proc = Process.sensible(n_proc);
this.generator = generator;
}
@Override
public Iterator<O> iterator() {
// Check whether the inputs where already consumed
if (!in.hasNext()) return null;
final ExecutorService exec = Utils.newFixedThreadPool(n_proc, ParallelMapping.class.getSimpleName());
final LinkedList<Future<O>> futures = new LinkedList<Future<O>>();
return new Iterator<O>() {
@Override
public boolean hasNext() {
final boolean b = !futures.isEmpty() || in.hasNext();
if (!b) {
exec.shutdown();
}
return b;
}
@Override
public O next() {
if (futures.size() < n_proc / 2) {
for (int i=0; i<n_proc; ++i) {
if (!in.hasNext()) {
exec.shutdown();
break;
}
futures.add(exec.submit(generator.create(in.next())));
}
}
try {
return futures.removeFirst().get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}