package org.infinispan.distexec; import java.util.Comparator; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionService; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.TimeUnit; import org.infinispan.remoting.transport.Address; /** * A {@link CompletionService} that uses a supplied {@link DistributedExecutorService} to execute * tasks. This class arranges that submitted tasks are, upon completion, placed on a queue * accessible using <tt>take</tt>. The class is lightweight enough to be suitable for transient use * when processing groups of tasks. * <p> * This class must be used instead of a {@link ExecutorCompletionService} provided from * java.util.concurrent package. The {@link ExecutorCompletionService} may not be used since it * requires the use of a non serializable RunnableFuture object. * * @author William Burns * @author Vladimir Blagojevic */ public class DistributedExecutionCompletionService<V> implements CompletionService<V> { protected final DistributedExecutorService executor; protected final BlockingQueue<CompletableFuture<V>> completionQueue; /** * Creates an ExecutorCompletionService using the supplied executor for base task execution and a * {@link LinkedBlockingQueue} as a completion queue. * * @param executor * the executor to use * @throws NullPointerException * if executor is <tt>null</tt> */ public DistributedExecutionCompletionService(DistributedExecutorService executor) { this(executor, null); } /** * Creates an ExecutorCompletionService using the supplied executor for base task execution and * the supplied queue as its completion queue. * * Note: {@link PriorityBlockingQueue} for completionQueue can only be used with accompanying * {@link Comparator} as our internal implementation of {@link Future} for each subtask does not * implement Comparable interface. Note that we do not provide any guarantees about which * particular internal class implements Future interface and these APIs will remain internal. * * @param executor * the executor to use * @param completionQueue * the queue to use as the completion queue normally one dedicated for use by this * service * @throws NullPointerException * if executor is <tt>null</tt> */ public DistributedExecutionCompletionService(DistributedExecutorService executor, BlockingQueue<CompletableFuture<V>> completionQueue) { if (executor == null) throw new NullPointerException(); this.executor = executor; if (completionQueue == null) { this.completionQueue = new LinkedBlockingQueue<CompletableFuture<V>>(); } else { this.completionQueue = completionQueue; } } /** * {@inheritDoc CompletionService} */ @Override public CompletableFuture<V> submit(Callable<V> task) { if (task == null) throw new NullPointerException(); CompletableFuture<V> f = (CompletableFuture<V>) executor.submit(task); return f.whenComplete((v, t) -> completionQueue.add(f)); } /** * {@inheritDoc CompletionService} */ @Override public CompletableFuture<V> submit(Runnable task, V result) { if (task == null) throw new NullPointerException(); CompletableFuture<V> f = (CompletableFuture<V>) executor.submit(task, result); return f.whenComplete((v, t) -> completionQueue.add(f)); } /** * {@inheritDoc CompletionService} */ @Override public CompletableFuture<V> take() throws InterruptedException { return completionQueue.take(); } /** * {@inheritDoc CompletionService} */ @Override public CompletableFuture<V> poll() { return completionQueue.poll(); } /** * {@inheritDoc CompletionService} */ @Override public CompletableFuture<V> poll(long timeout, TimeUnit unit) throws InterruptedException { return completionQueue.poll(timeout, unit); } public <K> Future<V> submit(Callable<V> task, K... input) { CompletableFuture<V> f = executor.submit(task, input); return f.whenComplete((v, t) -> completionQueue.add(f)); } public List<CompletableFuture<V>> submitEverywhere(Callable<V> task) { List<CompletableFuture<V>> fl = executor.submitEverywhere(task); for (Future<V> f : fl) { ((CompletableFuture<V>) f).whenComplete((v, t) -> completionQueue.add((CompletableFuture<V>) f)); } return fl; } public <K> List<CompletableFuture<V>> submitEverywhere(Callable<V> task, K... input) { List<CompletableFuture<V>> fl = executor.submitEverywhere(task, input); for (CompletableFuture<V> f : fl) { f.whenComplete((v, t) -> completionQueue.add(f)); } return fl; } public <K> CompletableFuture<V> submit(Address target, Callable<V> task) { CompletableFuture<V> f = executor.submit(target, task); return f.whenComplete((v, t) -> completionQueue.add(f)); } }