package de.invesdwin.util.collections.iterable.concurrent;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import de.invesdwin.util.collections.iterable.ACloseableIterator;
import de.invesdwin.util.collections.iterable.ICloseableIterator;
import de.invesdwin.util.concurrent.Executors;
import de.invesdwin.util.concurrent.Futures;
import de.invesdwin.util.concurrent.WrappedExecutorService;
import de.invesdwin.util.error.FastNoSuchElementException;
@ThreadSafe
public abstract class AParallelChunkConsumerIterator<R, E> extends ACloseableIterator<E> {
private static final int DEFAULT_CONSUMER_COUNT = Executors.getCpuThreadPoolCount();
private final ICloseableIterator<R> requests;
private final WrappedExecutorService consumerExecutor;
@GuardedBy("this")
private final List<Future<E>> futures;
private final int chunkSize;
public AParallelChunkConsumerIterator(final String name, final ICloseableIterator<R> requests) {
this(name, requests, DEFAULT_CONSUMER_COUNT);
}
public AParallelChunkConsumerIterator(final String name, final ICloseableIterator<R> requests,
final int chunkSize) {
this.chunkSize = chunkSize;
this.requests = requests;
this.consumerExecutor = Executors.newFixedThreadPool(name, chunkSize);
this.futures = new ArrayList<Future<E>>(chunkSize);
}
@Override
protected boolean innerHasNext() {
return requests.hasNext() || !futures.isEmpty();
}
@Override
protected synchronized E innerNext() {
while (requests.hasNext() && consumerExecutor.getPendingCount() < consumerExecutor.getFullPendingCount()
&& futures.size() < chunkSize) {
final R request = requests.next();
final Future<E> submit = consumerExecutor.submit(new Callable<E>() {
@Override
public E call() throws Exception {
return doWork(request);
}
});
futures.add(submit);
}
if (futures.isEmpty()) {
throw new FastNoSuchElementException("AParallelChunkConsumerIterator: futures is empty");
}
final Future<E> future = futures.remove(0);
try {
return Futures.get(future);
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
throw new FastNoSuchElementException("AParallelChunkConsumerIterator: InterrupedException received");
}
}
protected abstract E doWork(R request);
@Override
protected void innerClose() {
requests.close();
consumerExecutor.shutdown();
}
}