/****************************************************************************** * Copyright © 2013-2016 The Nxt Core Developers. * * * * See the AUTHORS.txt, DEVELOPER-AGREEMENT.txt and LICENSE.txt files at * * the top-level directory of this distribution for the individual copyright * * holder information and the developer policies on copyright and licensing. * * * * Unless otherwise agreed in a custom licensing agreement, no part of the * * Nxt software, including this file, may be copied, modified, propagated, * * or distributed except according to the terms contained in the LICENSE.txt * * file. * * * * Removal or modification of this copyright notice is prohibited. * * * ******************************************************************************/ package nxt.util; import nxt.Nxt; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public final class ThreadPool { private static volatile ScheduledExecutorService scheduledThreadPool; private static Map<Runnable,Long> backgroundJobs = new HashMap<>(); private static List<Runnable> beforeStartJobs = new ArrayList<>(); private static List<Runnable> lastBeforeStartJobs = new ArrayList<>(); private static List<Runnable> afterStartJobs = new ArrayList<>(); public static synchronized void runBeforeStart(Runnable runnable, boolean runLast) { if (scheduledThreadPool != null) { throw new IllegalStateException("Executor service already started"); } if (runLast) { lastBeforeStartJobs.add(runnable); } else { beforeStartJobs.add(runnable); } } public static synchronized void runAfterStart(Runnable runnable) { afterStartJobs.add(runnable); } public static synchronized void scheduleThread(String name, Runnable runnable, int delay) { scheduleThread(name, runnable, delay, TimeUnit.SECONDS); } public static synchronized void scheduleThread(String name, Runnable runnable, int delay, TimeUnit timeUnit) { if (scheduledThreadPool != null) { throw new IllegalStateException("Executor service already started, no new jobs accepted"); } if (! Nxt.getBooleanProperty("nxt.disable" + name + "Thread")) { backgroundJobs.put(runnable, timeUnit.toMillis(delay)); } else { Logger.logMessage("Will not run " + name + " thread"); } } public static synchronized void start(int timeMultiplier) { if (scheduledThreadPool != null) { throw new IllegalStateException("Executor service already started"); } Logger.logDebugMessage("Running " + beforeStartJobs.size() + " tasks..."); runAll(beforeStartJobs); beforeStartJobs = null; Logger.logDebugMessage("Running " + lastBeforeStartJobs.size() + " final tasks..."); runAll(lastBeforeStartJobs); lastBeforeStartJobs = null; Logger.logDebugMessage("Starting " + backgroundJobs.size() + " background jobs"); scheduledThreadPool = Executors.newScheduledThreadPool(backgroundJobs.size()); for (Map.Entry<Runnable,Long> entry : backgroundJobs.entrySet()) { scheduledThreadPool.scheduleWithFixedDelay(entry.getKey(), 0, Math.max(entry.getValue() / timeMultiplier, 1), TimeUnit.MILLISECONDS); } backgroundJobs = null; Logger.logDebugMessage("Starting " + afterStartJobs.size() + " delayed tasks"); Thread thread = new Thread() { @Override public void run() { runAll(afterStartJobs); afterStartJobs = null; } }; thread.setDaemon(true); thread.start(); } public static void shutdown() { if (scheduledThreadPool != null) { Logger.logShutdownMessage("Stopping background jobs..."); shutdownExecutor("scheduledThreadPool", scheduledThreadPool, 10); scheduledThreadPool = null; Logger.logShutdownMessage("...Done"); } } public static void shutdownExecutor(String name, ExecutorService executor, int timeout) { Logger.logShutdownMessage("shutting down " + name); executor.shutdown(); try { executor.awaitTermination(timeout, TimeUnit.SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } if (! executor.isTerminated()) { Logger.logShutdownMessage("some threads in " + name + " didn't terminate, forcing shutdown"); executor.shutdownNow(); } } private static void runAll(List<Runnable> jobs) { List<Thread> threads = new ArrayList<>(); final StringBuffer errors = new StringBuffer(); for (final Runnable runnable : jobs) { Thread thread = new Thread() { @Override public void run() { try { runnable.run(); } catch (Throwable t) { errors.append(t.getMessage()).append('\n'); throw t; } } }; thread.setDaemon(true); thread.start(); threads.add(thread); } for (Thread thread : threads) { try { thread.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } if (errors.length() > 0) { throw new RuntimeException("Errors running startup tasks:\n" + errors.toString()); } } private ThreadPool() {} //never }