diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index 695cd7b7a3..e6e1ba1c01 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -48,14 +48,14 @@ public class SnarkManager implements Snark.CompleteListener { private final Object _addSnarkLock; private /* FIXME final FIXME */ File _configFile; private Properties _config; - private I2PAppContext _context; - private Log _log; + private final I2PAppContext _context; + private final Log _log; private final List _messages; - private I2PSnarkUtil _util; + private final I2PSnarkUtil _util; private PeerCoordinatorSet _peerCoordinatorSet; private ConnectionAcceptor _connectionAcceptor; private Thread _monitor; - private boolean _running; + private volatile boolean _running; public static final String PROP_I2CP_HOST = "i2psnark.i2cpHost"; public static final String PROP_I2CP_PORT = "i2psnark.i2cpPort"; @@ -1089,7 +1089,7 @@ public class SnarkManager implements Snark.CompleteListener { // although the user will see the default until then getBWLimit(); boolean doMagnets = true; - while (true) { + while (_running) { File dir = getDataDir(); if (_log.shouldLog(Log.DEBUG)) _log.debug("Directory Monitor loop over " + dir.getAbsolutePath()); diff --git a/core/java/src/net/i2p/crypto/DHSessionKeyBuilder.java b/core/java/src/net/i2p/crypto/DHSessionKeyBuilder.java index 160cc1354b..fb3f6eefcf 100644 --- a/core/java/src/net/i2p/crypto/DHSessionKeyBuilder.java +++ b/core/java/src/net/i2p/crypto/DHSessionKeyBuilder.java @@ -53,7 +53,8 @@ public class DHSessionKeyBuilder { private static final int MAX_NUM_BUILDERS; private static final int CALC_DELAY; private static final LinkedBlockingQueue _builders; - private static final Thread _precalcThread; + private static Thread _precalcThread; + private static volatile boolean _isRunning; // the data of importance private BigInteger _myPrivateValue; @@ -96,12 +97,41 @@ public class DHSessionKeyBuilder { if (_log.shouldLog(Log.DEBUG)) _log.debug("DH Precalc (minimum: " + MIN_NUM_BUILDERS + " max: " + MAX_NUM_BUILDERS + ", delay: " + CALC_DELAY + ")"); + startPrecalc(); + } - _precalcThread = new I2PThread(new DHSessionKeyBuilderPrecalcRunner(MIN_NUM_BUILDERS, MAX_NUM_BUILDERS)); - _precalcThread.setName("DH Precalc"); - _precalcThread.setDaemon(true); - _precalcThread.setPriority(Thread.MIN_PRIORITY); - _precalcThread.start(); + /** @since 0.8.8 */ + private static void startPrecalc() { + synchronized(DHSessionKeyBuilder.class) { + _precalcThread = new I2PThread(new DHSessionKeyBuilderPrecalcRunner(MIN_NUM_BUILDERS, MAX_NUM_BUILDERS), + "DH Precalc", true); + _precalcThread.setPriority(Thread.MIN_PRIORITY); + _isRunning = true; + _precalcThread.start(); + } + } + + /** + * Note that this stops the singleton precalc thread. + * You don't want to do this if there are multiple routers in the JVM. + * Fix this if you care. See Router.shutdown(). + * @since 0.8.8 + */ + public static void shutdown() { + _isRunning = false; + _precalcThread.interrupt(); + _builders.clear(); + } + + /** + * Only required if shutdown() previously called. + * @since 0.8.8 + */ + public static void restart() { + synchronized(DHSessionKeyBuilder.class) { + if (!_isRunning) + startPrecalc(); + } } /** diff --git a/core/java/src/net/i2p/crypto/ElGamalEngine.java b/core/java/src/net/i2p/crypto/ElGamalEngine.java index 29f5df35a0..ba5d09f5be 100644 --- a/core/java/src/net/i2p/crypto/ElGamalEngine.java +++ b/core/java/src/net/i2p/crypto/ElGamalEngine.java @@ -78,6 +78,24 @@ public class ElGamalEngine { } + /** + * Note that this stops the singleton precalc thread. + * You don't want to do this if there are multiple routers in the JVM. + * Fix this if you care. See Router.shutdown(). + * @since 0.8.8 + */ + public void shutdown() { + YKGenerator.shutdown(); + } + + /** + * Only required if shutdown() previously called. + * @since 0.8.8 + */ + public static void restart() { + YKGenerator.restart(); + } + private final static BigInteger _two = new NativeBigInteger(1, new byte[] { 0x02}); private BigInteger[] getNextYK() { diff --git a/core/java/src/net/i2p/crypto/YKGenerator.java b/core/java/src/net/i2p/crypto/YKGenerator.java index ef8546e2f0..168ce1f5b2 100644 --- a/core/java/src/net/i2p/crypto/YKGenerator.java +++ b/core/java/src/net/i2p/crypto/YKGenerator.java @@ -42,8 +42,9 @@ class YKGenerator { private static final int MAX_NUM_BUILDERS; private static final int CALC_DELAY; private static final LinkedBlockingQueue _values; - private static final Thread _precalcThread; + private static Thread _precalcThread; private static final I2PAppContext ctx; + private static volatile boolean _isRunning; public final static String PROP_YK_PRECALC_MIN = "crypto.yk.precalc.min"; public final static String PROP_YK_PRECALC_MAX = "crypto.yk.precalc.max"; @@ -77,12 +78,41 @@ class YKGenerator { ctx.statManager().createRateStat("crypto.YKUsed", "Need a YK from the queue", "Encryption", new long[] { 60*60*1000 }); ctx.statManager().createRateStat("crypto.YKEmpty", "YK queue empty", "Encryption", new long[] { 60*60*1000 }); + startPrecalc(); + } - _precalcThread = new I2PThread(new YKPrecalcRunner(MIN_NUM_BUILDERS, MAX_NUM_BUILDERS)); - _precalcThread.setName("YK Precalc"); - _precalcThread.setDaemon(true); - _precalcThread.setPriority(Thread.MIN_PRIORITY); - _precalcThread.start(); + /** @since 0.8.8 */ + private static void startPrecalc() { + synchronized(YKGenerator.class) { + _precalcThread = new I2PThread(new YKPrecalcRunner(MIN_NUM_BUILDERS, MAX_NUM_BUILDERS), + "YK Precalc", true); + _precalcThread.setPriority(Thread.MIN_PRIORITY); + _isRunning = true; + _precalcThread.start(); + } + } + + /** + * Note that this stops the singleton precalc thread. + * You don't want to do this if there are multiple routers in the JVM. + * Fix this if you care. See Router.shutdown(). + * @since 0.8.8 + */ + public static void shutdown() { + _isRunning = false; + _precalcThread.interrupt(); + _values.clear(); + } + + /** + * Only required if shutdown() previously called. + * @since 0.8.8 + */ + public static void restart() { + synchronized(YKGenerator.class) { + if (!_isRunning) + startPrecalc(); + } } private static final int getSize() { @@ -161,7 +191,7 @@ class YKGenerator { } public void run() { - while (true) { + while (_isRunning) { int curSize = 0; //long start = Clock.getInstance().now(); int startSize = getSize(); @@ -172,7 +202,7 @@ class YKGenerator { _checkDelay += 1000; curSize = startSize; if (curSize < _minSize) { - for (int i = curSize; i < _maxSize; i++) { + for (int i = curSize; i < _maxSize && _isRunning; i++) { //long begin = Clock.getInstance().now(); if (!addValues(generateYK())) break; diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java index a6eb13ca42..b86e8ec22e 100644 --- a/router/java/src/net/i2p/router/Router.java +++ b/router/java/src/net/i2p/router/Router.java @@ -75,6 +75,8 @@ public class Router { private I2PThread.OOMEventListener _oomListener; private ShutdownHook _shutdownHook; private final I2PThread _gracefulShutdownDetector; + private final RouterWatchdog _watchdog; + private final Thread _watchdogThread; public final static String PROP_CONFIG_FILE = "router.configLocation"; @@ -277,8 +279,9 @@ public class Router { _gracefulShutdownDetector = new I2PAppThread(new GracefulShutdown(), "Graceful shutdown hook", true); _gracefulShutdownDetector.start(); - Thread watchdog = new I2PAppThread(new RouterWatchdog(_context), "RouterWatchdog", true); - watchdog.start(); + _watchdog = new RouterWatchdog(_context); + _watchdogThread = new I2PAppThread(_watchdog, "RouterWatchdog", true); + _watchdogThread.start(); } @@ -643,7 +646,10 @@ public class Router { private void warmupCrypto() { _context.random().nextBoolean(); - new DHSessionKeyBuilder(); // load the class so it starts the precalc process + // Use restart() to refire the static refiller threads, in case + // we are restarting the router in the same JVM (Android) + DHSessionKeyBuilder.restart(); + _context.elGamalEngine().restart(); } private void startupQueue() { @@ -977,11 +983,23 @@ public class Router { RouterContext.listContexts().remove(_context); // shut down I2PAppContext tasks here + + // TODO if there are multiple routers in the JVM, we don't want to do this + // to the DH or YK tasks, as they are singletons. + // Have MultiRouter set a property or something. + try { + DHSessionKeyBuilder.shutdown(); + } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting DH", t); } + try { + _context.elGamalEngine().shutdown(); + } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting elGamal", t); } try { ((FortunaRandomSource)_context.random()).shutdown(); } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting random()", t); } // logManager shut down in finalShutdown() + _watchdog.shutdown(); + _watchdogThread.interrupt(); finalShutdown(exitCode); } diff --git a/router/java/src/net/i2p/router/RouterWatchdog.java b/router/java/src/net/i2p/router/RouterWatchdog.java index a06a3c1c5c..f0d291755c 100644 --- a/router/java/src/net/i2p/router/RouterWatchdog.java +++ b/router/java/src/net/i2p/router/RouterWatchdog.java @@ -15,14 +15,21 @@ class RouterWatchdog implements Runnable { private final Log _log; private final RouterContext _context; private int _consecutiveErrors; + private volatile boolean _isRunning; private static final long MAX_JOB_RUN_LAG = 60*1000; public RouterWatchdog(RouterContext ctx) { _context = ctx; _log = ctx.logManager().getLog(RouterWatchdog.class); + _isRunning = true; } + /** @since 0.8.8 */ + public void shutdown() { + _isRunning = false; + } + public boolean verifyJobQueueLiveliness() { long when = _context.jobQueue().getLastJobBegin(); if (when < 0) @@ -109,7 +116,7 @@ class RouterWatchdog implements Runnable { } public void run() { - while (true) { + while (_isRunning) { try { Thread.sleep(60*1000); } catch (InterruptedException ie) {} monitorRouter(); } diff --git a/router/java/src/org/cybergarage/util/ThreadCore.java b/router/java/src/org/cybergarage/util/ThreadCore.java index b03f2f0c35..f2a49ddbeb 100644 --- a/router/java/src/org/cybergarage/util/ThreadCore.java +++ b/router/java/src/org/cybergarage/util/ThreadCore.java @@ -65,6 +65,8 @@ public class ThreadCore implements Runnable //threadObject.destroy(); //threadObject.stop(); setThreadObject(null); + // I2P break Disposer out of sleep() + threadObject.interrupt(); } }