package freenet.support;
import static java.util.concurrent.TimeUnit.MINUTES;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import freenet.node.PrioRunnable;
import freenet.support.Logger.LogLevel;
import freenet.support.io.NativeThread;
public class SerialExecutor implements Executor {
private static volatile boolean logMINOR;
static {
Logger.registerLogThresholdCallback(new LogThresholdCallback(){
@Override
public void shouldUpdate(){
logMINOR = Logger.shouldLog(LogLevel.MINOR, this);
}
});
}
private final LinkedBlockingQueue<Runnable> jobs;
private final Object syncLock;
private final int priority;
private volatile boolean threadWaiting;
private volatile boolean threadStarted;
private String name;
private Executor realExecutor;
private static final long NEWJOB_TIMEOUT = MINUTES.toMillis(5);
private Thread runningThread;
private final Runnable runner = new PrioRunnable() {
@Override
public int getPriority() {
return priority;
}
@Override
public void run() {
synchronized(syncLock) {
runningThread = Thread.currentThread();
}
try {
while(true) {
synchronized (syncLock) {
threadWaiting = true;
}
Runnable job = null;
try {
job = jobs.poll(NEWJOB_TIMEOUT, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
// ignore
}
synchronized (syncLock) {
threadWaiting=false;
}
if (job == null) {
synchronized (syncLock) {
threadStarted = false;
}
return;
}
try {
job.run();
} catch (Throwable t) {
Logger.error(this, "Caught "+t, t);
Logger.error(this, "While running "+job+" on "+this);
}
}
} finally {
synchronized(syncLock) {
runningThread = null;
}
}
}
};
public SerialExecutor(int priority) {
this(priority, 0);
}
public SerialExecutor(int priority, int bound) {
if(bound > 0)
jobs = new LinkedBlockingQueue<Runnable>(bound);
else
jobs = new LinkedBlockingQueue<Runnable>();
this.priority = priority;
this.syncLock = new Object();
}
public void start(Executor realExecutor, String name) {
assert(realExecutor != this);
this.realExecutor=realExecutor;
this.name=name;
synchronized (syncLock) {
if (!jobs.isEmpty())
reallyStart();
}
}
private void reallyStart() {
synchronized (syncLock) {
threadStarted=true;
}
if (logMINOR)
Logger.minor(this, "Starting thread... " + name + " : " + runner);
realExecutor.execute(runner, name);
}
@Override
public void execute(Runnable job) {
execute(job, "<noname>");
}
@Override
public void execute(Runnable job, String jobName) {
if (logMINOR)
Logger.minor(this, "Running " + jobName + " : " + job + " started=" + threadStarted + " waiting="
+ threadWaiting);
jobs.offer(job);
synchronized (syncLock) {
if (!threadStarted && realExecutor != null)
reallyStart();
}
}
@Override
public void execute(Runnable job, String jobName, boolean fromTicker) {
execute(job, jobName);
}
@Override
public int[] runningThreads() {
int[] retval = new int[NativeThread.JAVA_PRIORITY_RANGE+1];
if (threadStarted && !threadWaiting)
retval[priority] = 1;
return retval;
}
@Override
public int[] waitingThreads() {
int[] retval = new int[NativeThread.JAVA_PRIORITY_RANGE+1];
synchronized (syncLock) {
if(threadStarted && threadWaiting)
retval[priority] = 1;
}
return retval;
}
@Override
public int getWaitingThreadsCount() {
synchronized (syncLock) {
return (threadStarted && threadWaiting) ? 1 : 0;
}
}
public boolean onThread() {
synchronized(syncLock) {
return Thread.currentThread() == runningThread;
}
}
}