Mercurial Hosting > nabble
diff src/nabble/model/Executors.java @ 0:7ecd1a4ef557
add content
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Thu, 21 Mar 2019 19:15:52 -0600 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/nabble/model/Executors.java Thu Mar 21 19:15:52 2019 -0600 @@ -0,0 +1,160 @@ +package nabble.model; + +import java.util.Date; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import fschmidt.db.DbDatabase; +import fschmidt.util.executor.RunnableWrapper; +import fschmidt.util.executor.ThreadPool; +import fschmidt.util.executor.ScheduledThreadPool; +import fschmidt.util.executor.ThreadTimer; +import fschmidt.util.java.DateUtils; + + +public final class Executors { + private Executors() {} // never + + private static final Logger logger = LoggerFactory.getLogger(Executors.class); + + public static final ThreadTimer threadTimer = new ThreadTimer(); + + private static final RunnableWrapper dbCleanup = new RunnableWrapper() { + @Override public Runnable wrap(final Runnable command) { + return new Runnable(){public void run(){ + try { + command.run(); + } finally { + fschmidt.db.pool.Pool.threadReset(); + } + }}; + } + }; + + private static final ScheduledThreadPool backgroundExecutor = new ScheduledThreadPool(1); + static { + backgroundExecutor.addRunnableWrapper(dbCleanup); + backgroundExecutor.addRunnableWrapper(threadTimer); + backgroundExecutor.addRunnableWrapper(new TimedExecuteWrapper()); + } + public static final ThreadPool foregroundExecutor = new ThreadPool(Init.get("poolSize",100)); + static { + foregroundExecutor.addRunnableWrapper(dbCleanup); + foregroundExecutor.addRunnableWrapper(threadTimer); + } + private static Random rnd = new Random(); + + private static final long timeLimit = Init.get("execTimeLimitSeconds", 60L); + private static volatile boolean isShuttingDown = false; + private static long start = System.currentTimeMillis(); + + static void shutdown() { + isShuttingDown = true; + backgroundExecutor.shutdown(); + foregroundExecutor.shutdown(); + try { + while( !backgroundExecutor.awaitTermination(1,TimeUnit.SECONDS) ) { + logger.error("backgroundExecutor failed to shutdown"); + for( Thread thread : backgroundExecutor.getThreads() ) { + if( thread.isAlive() ) { + Throwable t = new Throwable(thread.toString()); + t.setStackTrace(thread.getStackTrace()); + logger.error("backgroundExecutor thread",t); + } + } + } +/* why does this matter? + while( !foregroundExecutor.awaitTermination(1,TimeUnit.SECONDS) ) { + logger.error("foregroundExecutor failed to shutdown"); + for( Thread thread : foregroundExecutor.getThreads() ) { + if( thread.isAlive() ) { + Throwable t = new Throwable(thread.toString()); + t.setStackTrace(thread.getStackTrace()); + logger.error("foregroundExecutor thread",t); + } + } + } +*/ + } catch(InterruptedException e) { + logger.error("",e); + } + } + + public static boolean isShuttingDown() { + long time = (System.currentTimeMillis() - start)/1000; + if( time > timeLimit ) + logger.error("exec took too long, " + time + " seconds",new Exception()); + start = System.currentTimeMillis(); + return isShuttingDown; + } + + private static final class TimedExecuteWrapper implements RunnableWrapper { + private ThreadLocal<Exception> trace = new ThreadLocal<Exception>() { + protected Exception initialValue() { + return new Exception("exec created"); + } + }; + + @Override public Runnable wrap(final Runnable command) { + return new Runnable(){public void run(){ + Thread.currentThread().setName("background-thread"); + trace.get(); + start = System.currentTimeMillis(); + try { + command.run(); + } finally { + long time = (System.currentTimeMillis() - start)/1000; + if( time > timeLimit ) + logger.error("exec took too long, " + time + " seconds",trace); + } + }}; + } + } + + public static void executeNow(Runnable command) { + foregroundExecutor.execute(command); + } + + + + public static void executeSometime(Runnable command) { + schedule(command,0,TimeUnit.SECONDS); + } + + public static void schedule(Runnable command,long delay,TimeUnit unit) { + backgroundExecutor.schedule(command,delay,unit); + } + + public static void scheduleWithFixedDelay(final Runnable command,long initialDelay,final long delay,final TimeUnit unit) { + Runnable repeatedCommand = new Runnable(){public void run(){ + command.run(); + schedule(this,delay,unit); + }}; + schedule(repeatedCommand,initialDelay,unit); + } + + public static void runDaily(Runnable task) { + scheduleWithFixedDelay(task, rnd.nextInt(60*60*24), 60*60*24, TimeUnit.SECONDS); + } + + private static final long MILLIS_PER_DAY = 1000L*60L*60L*24L; + + public static void runDaily(final Runnable task, int hour, int minute) { + final long millisAfterMidnight = ((long) hour * 60 + minute) * 60 * 1000; + Date now = new Date(); + long time = now.getTime() - DateUtils.roundToDay(now).getTime(); + long sleep = time < millisAfterMidnight + ? millisAfterMidnight - time + : MILLIS_PER_DAY - (time - millisAfterMidnight) + ; + scheduleWithFixedDelay(task, sleep/(1000L*60), 60*24, TimeUnit.MINUTES); + } + + public static void executeAfterCommit(DbDatabase db,final Runnable command) { + db.runAfterCommit(new Runnable(){public void run(){ + executeNow(command); + }}); + } + +}