package net.tuis.ubench; import java.util.concurrent.Callable; import java.util.concurrent.CompletionService; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; /** * Execution model that establishes a thread for each task, runs them all * concurrenly, but still fails (relatively) fast if any one task fails * * @author rolf * */ final class ParallelExecutionModel implements TaskExecutionModel, ThreadFactory { private static final AtomicInteger threadId = new AtomicInteger(); private static final class Combiner implements Callable<Combiner> { private final int index; private final TaskRunner runner; private final AtomicBoolean terminated; public Combiner(int index, TaskRunner runner, AtomicBoolean terminated) { super(); this.index = index; this.runner = runner; this.terminated = terminated; } @Override public Combiner call() throws Exception { boolean done = false; int loops = 0; do { loops++; if (loops % 1000 == 0) { // check each 1000 loops. if (terminated.get()) { return this; } } done = runner.invoke(); } while (!done); return this; } } @Override public final Thread newThread(Runnable r) { Thread t = new Thread(r, "Parallel Model " + threadId.incrementAndGet()); t.setDaemon(true); return t; } @Override public UStats[] executeTasks(String suite, TaskRunner...tasks) { UStats[] results = new UStats[tasks.length]; ExecutorService service = Executors.newFixedThreadPool(tasks.length, this); CompletionService<Combiner> completion = new ExecutorCompletionService<>(service); final AtomicBoolean terminator = new AtomicBoolean(false); try { for (int i = 0; i < tasks.length; i++) { completion.submit(new Combiner(i, tasks[i], terminator)); } for (int i = 0; i < tasks.length; i++) { Future<Combiner> fc = completion.take(); Combiner c = fc.get(); results[c.index] = c.runner.collect(suite); } return results; } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); throw new UBenchRuntimeException("Parallel Execution interrupted. See cause.", e); } catch (ExecutionException e) { Throwable cause = e.getCause(); if (cause instanceof UBenchRuntimeException) { throw (UBenchRuntimeException) cause; } throw new UBenchRuntimeException("Parallel Execution failed. See cause.", cause); } finally { terminator.set(true); service.shutdown(); if (!service.isTerminated()) { try { service.awaitTermination(1, TimeUnit.SECONDS); if (!service.isTerminated()) { throw new UBenchRuntimeException( "Unable to cleanly shut down the Parallel execution in 1 second"); } } catch (InterruptedException ie) { throw new UBenchRuntimeException("Parallel Execution interrupted. See cause.", ie); } } } } }