package com.hwlcn.ldap.util.parallel;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import com.hwlcn.ldap.util.Debug;
import com.hwlcn.core.annotation.InternalUseOnly;
import com.hwlcn.core.annotation.ThreadSafety;
import com.hwlcn.ldap.util.ThreadSafetyLevel;
@InternalUseOnly()
@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
public final class AsynchronousParallelProcessor<I, O>
{
private final BlockingQueue<I> pendingQueue;
private final ParallelProcessor<I, O> parallelProcessor;
private final ResultProcessor<I, O> resultProcessor;
private final InvokerThread invokerThread;
private final AtomicBoolean shutdown = new AtomicBoolean(false);
private final AtomicReference<Throwable> invocationException =
new AtomicReference<Throwable>();
public AsynchronousParallelProcessor(
final BlockingQueue<I> pendingQueue,
final ParallelProcessor<I, O> parallelProcessor,
final ResultProcessor<I, O> resultProcessor)
{
this.pendingQueue = pendingQueue;
this.parallelProcessor = parallelProcessor;
this.resultProcessor = resultProcessor;
this.invokerThread = new InvokerThread();
this.invokerThread.start();
}
public AsynchronousParallelProcessor(
final BlockingQueue<I> pendingQueue,
final ParallelProcessor<I, O> parallelProcessor,
final BlockingQueue<Result<I, O>> outputQueue)
{
this(pendingQueue, parallelProcessor,
new OutputEnqueuer<I, O>(outputQueue));
}
public synchronized void submit(final I input)
throws InterruptedException
{
if (shutdown.get())
{
throw new IllegalStateException("cannot call submit() after shutdown()");
}
final Throwable resultProcessingError = invocationException.get();
if (resultProcessingError != null)
{
shutdown();
throw new RuntimeException(resultProcessingError);
}
pendingQueue.put(input);
}
public synchronized void shutdown()
throws InterruptedException
{
if (shutdown.getAndSet(true))
{
return;
}
invokerThread.join();
parallelProcessor.shutdown();
}
private static final class OutputEnqueuer<I, O>
implements ResultProcessor<I, O>
{
private final BlockingQueue<Result<I, O>> outputQueue;
private OutputEnqueuer(final BlockingQueue<Result<I, O>> outputQueue)
{
this.outputQueue = outputQueue;
}
public void processResult(final Result<I, O> ioResult)
throws Exception
{
outputQueue.put(ioResult);
}
}
private final class InvokerThread
extends Thread
{
private InvokerThread()
{
super("Asynchronous Parallel Processor");
setDaemon(true);
}
@Override()
public void run()
{
while (!(shutdown.get() && pendingQueue.isEmpty()))
{
try
{
final I item = pendingQueue.poll(100, TimeUnit.MILLISECONDS);
if (item != null)
{
final List<I> items = new ArrayList<I>(1 + pendingQueue.size());
items.add(item);
pendingQueue.drainTo(items);
final List<Result<I, O>> results =
parallelProcessor.processAll(items);
for (final Result<I, O> result : results)
{
resultProcessor.processResult(result);
}
}
}
catch (Throwable e)
{
Debug.debugException(e);
invocationException.compareAndSet(null, e);
}
}
}
}
}