package org.httpkit.timer;
import clojure.lang.IFn;
import org.httpkit.HttpUtils;
import org.httpkit.PriorityQueue;
import java.util.concurrent.atomic.AtomicBoolean;
public class TimerService implements Runnable {
private final PriorityQueue<CancelableFutureTask> queue = new PriorityQueue<CancelableFutureTask>();
private final AtomicBoolean started = new AtomicBoolean(false);
public CancelableFutureTask scheduleTask(int timeout, IFn task) {
// start the timer thread, if not started
if (started.compareAndSet(false, true)) {
// the timer thread will kill itself when no job to schedule for too
// much time. restart if new job come it
Thread t = new Thread(this, "timer-service");
t.start();
}
CancelableFutureTask t = new CancelableFutureTask(timeout, task, queue);
synchronized (queue) {
queue.offer(t);
queue.notify();
}
return t;
}
public static final TimerService SERVICE = new TimerService();
@Override
public String toString() {
return "pending=" + queue.size() + ", thread started:" + started.get();
}
public void run() {
// if 2 checks of the queue, find it empty, stop self
boolean emptyQueueWaited = false;
CancelableFutureTask task;
while (true) {
synchronized (queue) {
task = queue.peek();
}
if (task == null) {
synchronized (queue) {
try {
// wait 2 minute before kill self
queue.wait(1000 * 120);
if (emptyQueueWaited) {
started.compareAndSet(true, false);
break; // die, will restart
} else {
emptyQueueWaited = true; // queue is empty
}
} catch (InterruptedException ignore) {
}
}
} else {
emptyQueueWaited = false;
long due = task.timeoutTs - System.currentTimeMillis();
// schedule to run in 1000ms, maybe run in 1000ms, 1001ms, ...
if (due <= 0) {
try {
task.runTask();
} catch (Exception e) {
HttpUtils.printError("In timer: " + task, e);
}
synchronized (queue) { // remove
if (task == queue.peek()) {
queue.poll(); // much faster
} else {
queue.remove(task);
}
}
} else {
synchronized (queue) {
try {
queue.wait(due); // others may notify you
} catch (InterruptedException ignore) {
// maybe more urgent job come in
}
}
}
}
}
}
}