diff --git a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java index 523690764e123d640911a4c76a32bc33846a7792..35145418aaacb4b9f8cdedd21dec095fc6c4d2a4 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java @@ -342,6 +342,7 @@ public class RouterConsoleRunner { } Thread t = new I2PAppThread(new StatSummarizer(), "StatSummarizer", true); + t.setPriority(Thread.NORM_PRIORITY - 1); t.start(); List<RouterContext> contexts = RouterContext.listContexts(); @@ -350,10 +351,12 @@ public class RouterConsoleRunner { NewsFetcher fetcher = NewsFetcher.getInstance(ctx); Thread newsThread = new I2PAppThread(fetcher, "NewsFetcher", true); + newsThread.setPriority(Thread.NORM_PRIORITY - 1); newsThread.start(); if (PluginStarter.pluginsEnabled(ctx)) { t = new I2PAppThread(new PluginStarter(ctx), "PluginStarter", true); + t.setPriority(Thread.NORM_PRIORITY - 1); t.start(); ctx.addShutdownTask(new PluginStopper(ctx)); } diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java index 5fd7452cf4f8cc9d65f0cd68f5a01bf0f3aff31e..6599a00d7b3d6d0a4ac9c149877b5da5350548ce 100644 --- a/router/java/src/net/i2p/router/Router.java +++ b/router/java/src/net/i2p/router/Router.java @@ -275,32 +275,16 @@ public class Router implements RouterClock.ClockShiftListener { _log.info("New router created with config file " + _configFilename); //_sessionKeyPersistenceHelper = new SessionKeyPersistenceHelper(_context); _killVMOnEnd = true; - _oomListener = new I2PThread.OOMEventListener() { - public void outOfMemory(OutOfMemoryError oom) { - clearCaches(); - _log.log(Log.CRIT, "Thread ran out of memory, shutting down I2P", oom); - // prevent multiple parallel shutdowns (when you OOM, you OOM a lot...) - if (_shutdownInProgress) - return; - for (int i = 0; i < 5; i++) { // try this 5 times, in case it OOMs - try { - _log.log(Log.CRIT, "free mem: " + Runtime.getRuntime().freeMemory() + - " total mem: " + Runtime.getRuntime().totalMemory()); - break; // w00t - } catch (OutOfMemoryError oome) { - // gobble - } - } - _log.log(Log.CRIT, "To prevent future shutdowns, increase wrapper.java.maxmemory in $I2P/wrapper.config"); - shutdown(EXIT_OOM); - } - }; + _oomListener = new OOMListener(_context); + _shutdownHook = new ShutdownHook(_context); - _gracefulShutdownDetector = new I2PAppThread(new GracefulShutdown(), "Graceful shutdown hook", true); + _gracefulShutdownDetector = new I2PAppThread(new GracefulShutdown(_context), "Graceful shutdown hook", true); + _gracefulShutdownDetector.setPriority(Thread.NORM_PRIORITY + 1); _gracefulShutdownDetector.start(); _watchdog = new RouterWatchdog(_context); _watchdogThread = new I2PAppThread(_watchdog, "RouterWatchdog", true); + _watchdogThread.setPriority(Thread.NORM_PRIORITY + 1); _watchdogThread.start(); } @@ -973,27 +957,6 @@ public class Router implements RouterClock.ClockShiftListener { } ******/ - /** - * A non-daemon thread to let - * the shutdown task get all the way to the end - * @since 0.8.8 - */ - private static class Spinner extends Thread { - - public Spinner() { - super(); - setName("Shutdown Spinner"); - setDaemon(false); - } - - @Override - public void run() { - try { - sleep(60*1000); - } catch (InterruptedException ie) {} - } - } - public static final int EXIT_GRACEFUL = 2; public static final int EXIT_HARD = 3; public static final int EXIT_OOM = 10; @@ -1170,13 +1133,27 @@ public class Router implements RouterClock.ClockShiftListener { _gracefulShutdownDetector.notifyAll(); } } + /** * What exit code do we plan on using when we shut down (or -1, if there isn't a graceful shutdown planned) */ public int scheduledGracefulExitCode() { return _gracefulExitCode; } + + /** + * Is a graceful shutdown in progress? This may be cancelled. + */ public boolean gracefulShutdownInProgress() { return (null != _config.get(PROP_SHUTDOWN_IN_PROGRESS)); } + + /** + * Is a final shutdown in progress? This may not be cancelled. + * @since 0.8.12 + */ + public boolean isFinalShutdownInProgress() { + return _shutdownInProgress; + } + /** How long until the graceful shutdown will kill us? */ public long getShutdownTimeRemaining() { if (_gracefulExitCode <= 0) return -1; // maybe Long.MAX_VALUE would be better? @@ -1189,51 +1166,6 @@ public class Router implements RouterClock.ClockShiftListener { return exp + 2*CLOCK_FUDGE_FACTOR - _context.clock().now(); } - /** - * Simple thread that sits and waits forever, managing the - * graceful shutdown "process" (describing it would take more text - * than just reading the code...) - * - */ - private class GracefulShutdown implements Runnable { - public void run() { - while (true) { - boolean shutdown = (null != _config.get(PROP_SHUTDOWN_IN_PROGRESS)); - if (shutdown) { - if (_gracefulExitCode == EXIT_HARD || _gracefulExitCode == EXIT_HARD_RESTART || - _context.tunnelManager().getParticipatingCount() <= 0) { - if (_gracefulExitCode == EXIT_HARD) - _log.log(Log.CRIT, "Shutting down after a brief delay"); - else if (_gracefulExitCode == EXIT_HARD_RESTART) - _log.log(Log.CRIT, "Restarting after a brief delay"); - else - _log.log(Log.CRIT, "Graceful shutdown progress - no more tunnels, safe to die"); - // Allow time for a UI reponse - try { - synchronized (Thread.currentThread()) { - Thread.currentThread().wait(2*1000); - } - } catch (InterruptedException ie) {} - shutdown(_gracefulExitCode); - return; - } else { - try { - synchronized (Thread.currentThread()) { - Thread.currentThread().wait(10*1000); - } - } catch (InterruptedException ie) {} - } - } else { - try { - synchronized (Thread.currentThread()) { - Thread.currentThread().wait(); - } - } catch (InterruptedException ie) {} - } - } - } - } - /** * Save the current config options (returning true if save was * successful, false otherwise) @@ -1308,48 +1240,20 @@ public class Router implements RouterClock.ClockShiftListener { return; ((RouterClock) _context.clock()).removeShiftListener(this); _isAlive = false; - Thread t = new Thread(new Restarter(), "Router Restart"); + _started = _context.clock().now(); + Thread t = new Thread(new Restarter(_context), "Router Restart"); + t.setPriority(Thread.NORM_PRIORITY + 1); t.start(); } /** - * @since 0.8.8 + * Only for Restarter + * @since 0.8.12 */ - private class Restarter implements Runnable { - public void run() { - _started = _context.clock().now(); - _log.error("Stopping the router for a restart..."); - _log.logAlways(Log.WARN, "Stopping the client manager"); - // NOTE: DisconnectMessageHandler keys off "restart" - try { _context.clientManager().shutdown("Router restart"); } catch (Throwable t) { _log.log(Log.CRIT, "Error stopping the client manager", t); } - _log.logAlways(Log.WARN, "Stopping the comm system"); - _context.bandwidthLimiter().reinitialize(); - try { _context.messageRegistry().restart(); } catch (Throwable t) { _log.log(Log.CRIT, "Error restarting the message registry", t); } - try { _context.commSystem().restart(); } catch (Throwable t) { _log.log(Log.CRIT, "Error restarting the comm system", t); } - _log.logAlways(Log.WARN, "Stopping the tunnel manager"); - try { _context.tunnelManager().restart(); } catch (Throwable t) { _log.log(Log.CRIT, "Error restarting the tunnel manager", t); } - - //try { _context.peerManager().restart(); } catch (Throwable t) { _log.log(Log.CRIT, "Error restarting the peer manager", t); } - //try { _context.netDb().restart(); } catch (Throwable t) { _log.log(Log.CRIT, "Error restarting the networkDb", t); } - //try { _context.jobQueue().restart(); } catch (Throwable t) { _log.log(Log.CRIT, "Error restarting the job queue", t); } - - _log.logAlways(Log.WARN, "Router teardown complete, restarting the router..."); - try { Thread.sleep(10*1000); } catch (InterruptedException ie) {} - - _log.logAlways(Log.WARN, "Restarting the comm system"); - _log.logAlways(Log.WARN, "Restarting the tunnel manager"); - _log.logAlways(Log.WARN, "Restarting the client manager"); - try { _context.clientMessagePool().restart(); } catch (Throwable t) { _log.log(Log.CRIT, "Error restarting the CMP", t); } - try { _context.clientManager().startup(); } catch (Throwable t) { _log.log(Log.CRIT, "Error starting the client manager", t); } - - _isAlive = true; - rebuildRouterInfo(); - - _log.logAlways(Log.WARN, "Restart complete"); - ((RouterClock) _context.clock()).addShiftListener(Router.this); - } + public void setIsAlive() { + _isAlive = true; } - + public static void main(String args[]) { System.out.println("Starting I2P " + RouterVersion.FULL_VERSION); // installUpdates() moved to constructor so we can get file locations from the context diff --git a/router/java/src/net/i2p/router/tasks/GracefulShutdown.java b/router/java/src/net/i2p/router/tasks/GracefulShutdown.java new file mode 100644 index 0000000000000000000000000000000000000000..c2ded9b49131698f975243a8be8c3b77e66f53ce --- /dev/null +++ b/router/java/src/net/i2p/router/tasks/GracefulShutdown.java @@ -0,0 +1,60 @@ +package net.i2p.router.tasks; + +import net.i2p.router.Router; +import net.i2p.router.RouterContext; +import net.i2p.util.Log; + +/** + * Simple thread that sits and waits forever, managing the + * graceful shutdown "process" (describing it would take more text + * than just reading the code...) + * + * @since 0.8.12 moved from Router + */ +public class GracefulShutdown implements Runnable { + private final RouterContext _context; + + public GracefulShutdown(RouterContext ctx) { + _context = ctx; + } + + public void run() { + Log log = _context.logManager().getLog(Router.class); + while (true) { + boolean shutdown = _context.router().gracefulShutdownInProgress(); + if (shutdown) { + int gracefulExitCode = _context.router().scheduledGracefulExitCode(); + if (gracefulExitCode == Router.EXIT_HARD || gracefulExitCode == Router.EXIT_HARD_RESTART || + _context.tunnelManager().getParticipatingCount() <= 0) { + if (gracefulExitCode == Router.EXIT_HARD) + log.log(Log.CRIT, "Shutting down after a brief delay"); + else if (gracefulExitCode == Router.EXIT_HARD_RESTART) + log.log(Log.CRIT, "Restarting after a brief delay"); + else + log.log(Log.CRIT, "Graceful shutdown progress - no more tunnels, safe to die"); + // Allow time for a UI reponse + try { + synchronized (Thread.currentThread()) { + Thread.currentThread().wait(2*1000); + } + } catch (InterruptedException ie) {} + _context.router().shutdown(gracefulExitCode); + return; + } else { + try { + synchronized (Thread.currentThread()) { + Thread.currentThread().wait(10*1000); + } + } catch (InterruptedException ie) {} + } + } else { + try { + synchronized (Thread.currentThread()) { + Thread.currentThread().wait(); + } + } catch (InterruptedException ie) {} + } + } + } +} + diff --git a/router/java/src/net/i2p/router/tasks/OOMListener.java b/router/java/src/net/i2p/router/tasks/OOMListener.java new file mode 100644 index 0000000000000000000000000000000000000000..a9650c7bd2043d61e849217c2986dced3617d2c2 --- /dev/null +++ b/router/java/src/net/i2p/router/tasks/OOMListener.java @@ -0,0 +1,39 @@ +package net.i2p.router.tasks; + +import net.i2p.router.Router; +import net.i2p.router.RouterContext; +import net.i2p.util.I2PThread; +import net.i2p.util.Log; + +/** + * Kaboom + * + * @since 0.8.12 moved from Router.java + */ +public class OOMListener implements I2PThread.OOMEventListener { + private final RouterContext _context; + + public OOMListener(RouterContext ctx) { + _context = ctx; + } + + public void outOfMemory(OutOfMemoryError oom) { + Router.clearCaches(); + Log log = _context.logManager().getLog(Router.class); + log.log(Log.CRIT, "Thread ran out of memory, shutting down I2P", oom); + // prevent multiple parallel shutdowns (when you OOM, you OOM a lot...) + if (_context.router().isFinalShutdownInProgress()) + return; + for (int i = 0; i < 5; i++) { // try this 5 times, in case it OOMs + try { + log.log(Log.CRIT, "free mem: " + Runtime.getRuntime().freeMemory() + + " total mem: " + Runtime.getRuntime().totalMemory()); + break; // w00t + } catch (OutOfMemoryError oome) { + // gobble + } + } + log.log(Log.CRIT, "To prevent future shutdowns, increase wrapper.java.maxmemory in $I2P/wrapper.config"); + _context.router().shutdown(Router.EXIT_OOM); + } +} diff --git a/router/java/src/net/i2p/router/tasks/Restarter.java b/router/java/src/net/i2p/router/tasks/Restarter.java new file mode 100644 index 0000000000000000000000000000000000000000..2d497abb4b4a7fbc54735d7197d77fef79141ad2 --- /dev/null +++ b/router/java/src/net/i2p/router/tasks/Restarter.java @@ -0,0 +1,51 @@ +package net.i2p.router.tasks; + +import net.i2p.router.Router; +import net.i2p.router.RouterClock; +import net.i2p.router.RouterContext; +import net.i2p.util.Log; + +/** + * @since 0.8.8, moved from Router in 0.8.12 + */ +public class Restarter implements Runnable { + private final RouterContext _context; + + public Restarter(RouterContext ctx) { + _context = ctx; + } + + public void run() { + Log log = _context.logManager().getLog(Router.class); + log.error("Stopping the router for a restart..."); + log.logAlways(Log.WARN, "Stopping the client manager"); + // NOTE: DisconnectMessageHandler keys off "restart" + try { _context.clientManager().shutdown("Router restart"); } catch (Throwable t) { log.log(Log.CRIT, "Error stopping the client manager", t); } + log.logAlways(Log.WARN, "Stopping the comm system"); + _context.bandwidthLimiter().reinitialize(); + try { _context.messageRegistry().restart(); } catch (Throwable t) { log.log(Log.CRIT, "Error restarting the message registry", t); } + try { _context.commSystem().restart(); } catch (Throwable t) { log.log(Log.CRIT, "Error restarting the comm system", t); } + log.logAlways(Log.WARN, "Stopping the tunnel manager"); + try { _context.tunnelManager().restart(); } catch (Throwable t) { log.log(Log.CRIT, "Error restarting the tunnel manager", t); } + + //try { _context.peerManager().restart(); } catch (Throwable t) { log.log(Log.CRIT, "Error restarting the peer manager", t); } + //try { _context.netDb().restart(); } catch (Throwable t) { log.log(Log.CRIT, "Error restarting the networkDb", t); } + //try { _context.jobQueue().restart(); } catch (Throwable t) { log.log(Log.CRIT, "Error restarting the job queue", t); } + + log.logAlways(Log.WARN, "Router teardown complete, restarting the router..."); + try { Thread.sleep(10*1000); } catch (InterruptedException ie) {} + + log.logAlways(Log.WARN, "Restarting the comm system"); + log.logAlways(Log.WARN, "Restarting the tunnel manager"); + log.logAlways(Log.WARN, "Restarting the client manager"); + try { _context.clientMessagePool().restart(); } catch (Throwable t) { log.log(Log.CRIT, "Error restarting the CMP", t); } + try { _context.clientManager().startup(); } catch (Throwable t) { log.log(Log.CRIT, "Error starting the client manager", t); } + + _context.router().setIsAlive(); + _context.router().rebuildRouterInfo(); + + log.logAlways(Log.WARN, "Restart complete"); + ((RouterClock) _context.clock()).addShiftListener(_context.router()); + } +} +