forked from I2P_Developers/i2p.i2p
* Shutdown:
- Kill the global app context - Recognize multi-router case - Fix RandomIterator, YKGenerator, DHBuilder, NTCPConnection hanging on to old context - probably other offenders not yet found - Fix DHBuilder thread not stopping
This commit is contained in:
@ -115,6 +115,10 @@ public class I2PAppContext {
|
||||
* Pull the default context, creating a new one if necessary, else using
|
||||
* the first one created.
|
||||
*
|
||||
* Warning - do not save the returned value, or the value of any methods below,
|
||||
* in a static field, or you will get the old context if a new router is
|
||||
* started in the same JVM after the first is shut down,
|
||||
* e.g. on Android.
|
||||
*/
|
||||
public static I2PAppContext getGlobalContext() {
|
||||
// skip the global lock - _gAC must be volatile
|
||||
@ -165,8 +169,12 @@ public class I2PAppContext {
|
||||
private I2PAppContext(boolean doInit, Properties envProps) {
|
||||
if (doInit) {
|
||||
synchronized (I2PAppContext.class) {
|
||||
if (_globalAppContext == null)
|
||||
if (_globalAppContext == null) {
|
||||
_globalAppContext = this;
|
||||
} else {
|
||||
System.out.println("Warning - New context not replacing old one, you now have a second one");
|
||||
(new Exception("I did it")).printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
_overrideProps = new I2PProperties();
|
||||
@ -186,7 +194,7 @@ public class I2PAppContext {
|
||||
_elGamalAESEngineInitialized = false;
|
||||
_logManagerInitialized = false;
|
||||
_keyRingInitialized = false;
|
||||
_shutdownTasks = new ConcurrentHashSet(16);
|
||||
_shutdownTasks = new ConcurrentHashSet(32);
|
||||
initializeDirs();
|
||||
}
|
||||
|
||||
|
@ -47,8 +47,8 @@ import net.i2p.util.RandomSource;
|
||||
* @author jrandom
|
||||
*/
|
||||
public class DHSessionKeyBuilder {
|
||||
private static final I2PAppContext _context = I2PAppContext.getGlobalContext();
|
||||
private static final Log _log;
|
||||
private static I2PAppContext _context = I2PAppContext.getGlobalContext();
|
||||
private static Log _log;
|
||||
private static final int MIN_NUM_BUILDERS;
|
||||
private static final int MAX_NUM_BUILDERS;
|
||||
private static final int CALC_DELAY;
|
||||
@ -100,15 +100,18 @@ public class DHSessionKeyBuilder {
|
||||
startPrecalc();
|
||||
}
|
||||
|
||||
/** @since 0.8.8 */
|
||||
/**
|
||||
* Caller must synch on class
|
||||
* @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();
|
||||
}
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
_log = _context.logManager().getLog(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();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -505,7 +508,7 @@ public class DHSessionKeyBuilder {
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while (true) {
|
||||
while (_isRunning) {
|
||||
|
||||
int curSize = 0;
|
||||
long start = System.currentTimeMillis();
|
||||
|
@ -43,7 +43,7 @@ class YKGenerator {
|
||||
private static final int CALC_DELAY;
|
||||
private static final LinkedBlockingQueue<BigInteger[]> _values;
|
||||
private static Thread _precalcThread;
|
||||
private static final I2PAppContext ctx;
|
||||
private static I2PAppContext ctx;
|
||||
private static volatile boolean _isRunning;
|
||||
|
||||
public final static String PROP_YK_PRECALC_MIN = "crypto.yk.precalc.min";
|
||||
@ -76,20 +76,22 @@ class YKGenerator {
|
||||
// _log.debug("ElGamal YK Precalc (minimum: " + MIN_NUM_BUILDERS + " max: " + MAX_NUM_BUILDERS + ", delay: "
|
||||
// + CALC_DELAY + ")");
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/** @since 0.8.8 */
|
||||
/**
|
||||
* Caller must synch on class
|
||||
* @since 0.8.8
|
||||
*/
|
||||
private static void startPrecalc() {
|
||||
synchronized(YKGenerator.class) {
|
||||
_precalcThread = new I2PThread(new YKPrecalcRunner(MIN_NUM_BUILDERS, MAX_NUM_BUILDERS),
|
||||
ctx = I2PAppContext.getGlobalContext();
|
||||
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 });
|
||||
_precalcThread = new I2PThread(new YKPrecalcRunner(MIN_NUM_BUILDERS, MAX_NUM_BUILDERS),
|
||||
"YK Precalc", true);
|
||||
_precalcThread.setPriority(Thread.MIN_PRIORITY);
|
||||
_isRunning = true;
|
||||
_precalcThread.start();
|
||||
}
|
||||
_precalcThread.setPriority(Thread.MIN_PRIORITY);
|
||||
_isRunning = true;
|
||||
_precalcThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,8 +22,8 @@ import net.i2p.util.Log;
|
||||
*
|
||||
*/
|
||||
public class I2NPMessageHandler {
|
||||
private Log _log;
|
||||
private I2PAppContext _context;
|
||||
private final Log _log;
|
||||
private final I2PAppContext _context;
|
||||
private long _lastReadBegin;
|
||||
private long _lastReadEnd;
|
||||
private int _lastSize;
|
||||
|
@ -28,8 +28,8 @@ import net.i2p.util.SimpleByteCache;
|
||||
* @author jrandom
|
||||
*/
|
||||
public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPMessage {
|
||||
private Log _log;
|
||||
protected I2PAppContext _context;
|
||||
private final Log _log;
|
||||
protected final I2PAppContext _context;
|
||||
private long _expiration;
|
||||
private long _uniqueId;
|
||||
|
||||
|
@ -980,19 +980,23 @@ public class Router {
|
||||
try { _context.inNetMessagePool().shutdown(); } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting down the inbound net pool", t); }
|
||||
//try { _sessionKeyPersistenceHelper.shutdown(); } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting down the session key manager", t); }
|
||||
_context.deleteTempDir();
|
||||
RouterContext.listContexts().remove(_context);
|
||||
List<RouterContext> contexts = RouterContext.getContexts();
|
||||
contexts.remove(_context);
|
||||
|
||||
// shut down I2PAppContext tasks here
|
||||
|
||||
// TODO if there are multiple routers in the JVM, we don't want to do this
|
||||
// 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); }
|
||||
if (contexts.isEmpty()) {
|
||||
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); }
|
||||
} else {
|
||||
_log.logAlways(Log.WARN, "Warning - " + contexts.size() + " routers remaining in this JVM, not releasing all resources");
|
||||
}
|
||||
try {
|
||||
((FortunaRandomSource)_context.random()).shutdown();
|
||||
} catch (Throwable t) { _log.log(Log.CRIT, "Error shutting random()", t); }
|
||||
@ -1020,6 +1024,8 @@ public class Router {
|
||||
|
||||
File f = getPingFile();
|
||||
f.delete();
|
||||
if (RouterContext.getContexts().isEmpty())
|
||||
RouterContext.killGlobalContext();
|
||||
if (_killVMOnEnd) {
|
||||
try { Thread.sleep(1000); } catch (InterruptedException ie) {}
|
||||
Runtime.getRuntime().halt(exitCode);
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.i2p.router;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
@ -67,6 +68,8 @@ public class RouterContext extends I2PAppContext {
|
||||
// to init everything. Caller MUST call initAll() afterwards.
|
||||
// Sorry, this breaks some main() unit tests out there.
|
||||
//initAll();
|
||||
if (!_contexts.isEmpty())
|
||||
System.out.println("Warning - More than one router in this JVM");
|
||||
_contexts.add(this);
|
||||
}
|
||||
|
||||
@ -165,11 +168,37 @@ public class RouterContext extends I2PAppContext {
|
||||
/**
|
||||
* Retrieve the list of router contexts currently instantiated in this JVM.
|
||||
* This will always contain only one item (except when a simulation per the
|
||||
* MultiRouter is going on), and the list should only be modified when a new
|
||||
* MultiRouter is going on).
|
||||
*
|
||||
* @return an unmodifiable list (as of 0.8.8). May be null or empty.
|
||||
*/
|
||||
public static List<RouterContext> listContexts() {
|
||||
return Collections.unmodifiableList(_contexts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as listContexts() but package private and modifiable.
|
||||
* The list should only be modified when a new
|
||||
* context is created or a router is shut down.
|
||||
*
|
||||
* @since 0.8.8
|
||||
*/
|
||||
public static List<RouterContext> listContexts() { return _contexts; }
|
||||
static List<RouterContext> getContexts() {
|
||||
return _contexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill the global I2PAppContext, so it isn't still around
|
||||
* when we restart in the same JVM (Android).
|
||||
* Only do this if there are no other routers in the JVM.
|
||||
*
|
||||
* @since 0.8.8
|
||||
*/
|
||||
static void killGlobalContext() {
|
||||
synchronized (I2PAppContext.class) {
|
||||
_globalAppContext = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** what router is this context working for? */
|
||||
public Router router() { return _router; }
|
||||
|
@ -1089,6 +1089,10 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
||||
}
|
||||
|
||||
private static final int MAX_HANDLERS = 4;
|
||||
|
||||
/**
|
||||
* FIXME static queue mixes handlers from different contexts in multirouter JVM
|
||||
*/
|
||||
private final static LinkedBlockingQueue<I2NPMessageHandler> _i2npHandlers = new LinkedBlockingQueue(MAX_HANDLERS);
|
||||
|
||||
private final static I2NPMessageHandler acquireHandler(RouterContext ctx) {
|
||||
@ -1129,6 +1133,15 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
||||
_dataReadBufs.offer(buf);
|
||||
}
|
||||
|
||||
/** @since 0.8.8 */
|
||||
static void releaseResources() {
|
||||
_i2npHandlers.clear();
|
||||
_dataReadBufs.clear();
|
||||
synchronized(_bufs) {
|
||||
_bufs.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sizeof(data)+data+pad+crc.
|
||||
*
|
||||
|
@ -702,6 +702,7 @@ public class NTCPTransport extends TransportImpl {
|
||||
NTCPConnection con = (NTCPConnection)iter.next();
|
||||
con.close();
|
||||
}
|
||||
NTCPConnection.releaseResources();
|
||||
// will this work?
|
||||
replaceAddress(null);
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ public class RandomIterator<E> implements Iterator<E> {
|
||||
* <a href="http://www.qbrundage.com/michaelb/pubs/essays/random_number_generation" title="http://www.qbrundage.com/michaelb/pubs/essays/random_number_generation" target="_blank">http://www.qbrundage.com/michaelb/pubs/e…</a>
|
||||
* for some implementations, which are faster than java.util.Random.
|
||||
*/
|
||||
private static final Random rand = RandomSource.getInstance();
|
||||
private final Random rand = RandomSource.getInstance();
|
||||
|
||||
/** Used to narrow the range to take random indexes from */
|
||||
private int lower, upper;
|
||||
|
Reference in New Issue
Block a user