view 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 source

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

}