package com.github.sarxos.webcam;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WebcamProcessor {
private static final Logger LOG = LoggerFactory.getLogger(WebcamProcessor.class);
/**
* Thread doing supersync processing.
*
* @author sarxos
*/
public static final class ProcessorThread extends Thread {
private static final AtomicInteger N = new AtomicInteger(0);
public ProcessorThread(Runnable r) {
super(r, String.format("atomic-processor-%d", N.incrementAndGet()));
}
}
/**
* Thread factory for processor.
*
* @author Bartosz Firyn (SarXos)
*/
private static final class ProcessorThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
Thread t = new ProcessorThread(r);
t.setUncaughtExceptionHandler(WebcamExceptionHandler.getInstance());
t.setDaemon(true);
return t;
}
}
/**
* Heart of overall processing system. This class process all native calls wrapped in tasks, by
* doing this all tasks executions are super-synchronized.
*
* @author Bartosz Firyn (SarXos)
*/
private static final class AtomicProcessor implements Runnable {
private SynchronousQueue<WebcamTask> inbound = new SynchronousQueue<WebcamTask>(true);
private SynchronousQueue<WebcamTask> outbound = new SynchronousQueue<WebcamTask>(true);
/**
* Process task.
*
* @param task the task to be processed
* @throws InterruptedException when thread has been interrupted
*/
public void process(WebcamTask task) throws InterruptedException {
inbound.put(task);
Throwable t = outbound.take().getThrowable();
if (t != null) {
throw new WebcamException("Cannot execute task", t);
}
}
@Override
public void run() {
while (true) {
WebcamTask t = null;
try {
(t = inbound.take()).handle();
} catch (InterruptedException e) {
break;
} catch (Throwable e) {
if (t != null) {
t.setThrowable(e);
}
} finally {
if (t != null) {
try {
outbound.put(t);
} catch (InterruptedException e) {
break;
} catch (Exception e) {
throw new RuntimeException("Cannot put task into outbound queue", e);
}
}
}
}
}
}
/**
* Is processor started?
*/
private static final AtomicBoolean started = new AtomicBoolean(false);
/**
* Execution service.
*/
private static ExecutorService runner = null;
/**
* Static processor.
*/
private static final AtomicProcessor processor = new AtomicProcessor();
/**
* Singleton instance.
*/
private static final WebcamProcessor INSTANCE = new WebcamProcessor();;
private WebcamProcessor() {
}
/**
* Process single webcam task.
*
* @param task the task to be processed
* @throws InterruptedException when thread has been interrupted
*/
public void process(WebcamTask task) throws InterruptedException {
if (started.compareAndSet(false, true)) {
runner = Executors.newSingleThreadExecutor(new ProcessorThreadFactory());
runner.execute(processor);
}
if (!runner.isShutdown()) {
processor.process(task);
} else {
throw new RejectedExecutionException("Cannot process because processor runner has been already shut down");
}
}
public void shutdown() {
if (started.compareAndSet(true, false)) {
LOG.debug("Shutting down webcam processor");
runner.shutdown();
LOG.debug("Awaiting tasks termination");
while (runner.isTerminated()) {
try {
runner.awaitTermination(100, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
return;
}
runner.shutdownNow();
}
LOG.debug("All tasks has been terminated");
}
}
public static synchronized WebcamProcessor getInstance() {
return INSTANCE;
}
}