package spimedb.util;
import jcog.Texts;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by me on 2/3/17.
*/
public class PrioritizedExecutor implements Executor {
final static Logger logger = LoggerFactory.getLogger(PrioritizedExecutor.class);
private static final float DEFAULT_PRIORITY = 0.5f;
private static final long DEFAULT_TIMEOUT_ms = 5 * 60 * 1000;
public final PriorityBlockingQueue pq = new PriorityBlockingQueue<>(
512 * 1024,
runCompare);
public final ExecutorService exe;
public AtomicInteger running = new AtomicInteger();
public PrioritizedExecutor(int threads) {
//similar to Fixed-Size threadpool
this.exe = new ThreadPoolExecutor(threads, threads, 0, TimeUnit.MILLISECONDS, pq) {
@Override
protected void beforeExecute(Thread t, Runnable r) {
running.incrementAndGet();
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
running.decrementAndGet();
}
};
Runtime.getRuntime().addShutdownHook(new Thread(this::onShutDown));
}
protected void onShutDown() {
pq.forEach(x -> {
logger.info("not executed: {}", x);
});
}
public void run(float pri, Runnable r) {
run(new MyRunWithPriority(pri, r, DEFAULT_TIMEOUT_ms));
}
public void run(RunWithPriority r) {
exe.execute(r);
}
@Override
public void execute(@NotNull Runnable command) {
run(DEFAULT_PRIORITY, command);
}
public Map summary() {
Map x = new TreeMap();
x.put("pending", pq.size());
x.put("running", running.get());
return x;
}
public interface RunWithPriority extends Runnable {
float pri();
}
static final Comparator<RunWithPriority> runCompare = (x, y) -> {
if (x == y) return 0;
int c = Float.compare(y.pri(), x.pri());
if (c == 0) {
return Integer.compare(x.hashCode(), y.hashCode());
}
return c;
};
final Timer timer = new Timer(true);
class MyRunWithPriority implements RunWithPriority {
private final float pri;
private final Runnable r;
private final long timeoutMS;
public MyRunWithPriority(float pri, Runnable r, long timeoutMS) {
this.pri = pri;
this.timeoutMS = timeoutMS;
this.r = r;
}
@Override
public float pri() {
return pri;
}
@Override
public void run() {
TimeOutTask timeout = new TimeOutTask(Thread.currentThread());
timer.schedule(timeout, timeoutMS);
try {
r.run();
} catch (ThreadDeath ie) {
logger.error("{} interrupted {}", r, ie.getMessage());
}
timeout.cancel();
}
@Override
public String toString() {
return Texts.n2(100f * pri) + "%:" + r.toString();
}
}
static class TimeOutTask extends TimerTask {
final Thread t;
TimeOutTask(Thread t) {
this.t = t;
}
public void run() {
if (t != null && t.isAlive()) {
t.stop();
}
}
}
// public static void main(String args[]) {
// new PoolTest();
// }
// public PoolTest()
// {
// try
// {
// PooledExecutor pe = new PooledExecutor(3);
// pe.execute(new MyRunnable());
// pe.shutdownAfterProcessingCurrentlyQueuedTasks();
// }
// catch (Exception e)
// {
// e.printStackTrace();
// }
// }
}