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);
+		}});
+	}
+
+}