forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p.zzz.android' (head cbf2d39e1944b9d601558761d0eedcdebfd2f589)
to branch 'i2p.i2p' (head c2393e50afccfd5682a9086f0eec2a0700cda2c9)
This commit is contained in:
@ -2,6 +2,7 @@ package gnu.crypto.prng;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
@ -10,6 +11,12 @@ import net.i2p.util.Log;
|
||||
* fortuna instance that tries to avoid blocking if at all possible by using separate
|
||||
* filled buffer segments rather than one buffer (and blocking when that buffer's data
|
||||
* has been eaten)
|
||||
*
|
||||
* Note that this class is not fully Thread safe!
|
||||
* The following methods must be synchronized externally, they are not
|
||||
* sycned here or in super():
|
||||
* addRandomByte(), addRandomBytes(), nextByte(), nextBytes(), seed()
|
||||
*
|
||||
*/
|
||||
public class AsyncFortunaStandalone extends FortunaStandalone implements Runnable {
|
||||
/**
|
||||
@ -19,25 +26,23 @@ public class AsyncFortunaStandalone extends FortunaStandalone implements Runnabl
|
||||
private static final int DEFAULT_BUFFERS = 2;
|
||||
private static final int DEFAULT_BUFSIZE = 256*1024;
|
||||
private final int _bufferCount;
|
||||
private final byte asyncBuffers[][];
|
||||
private final int status[];
|
||||
private int nextBuf = 0;
|
||||
private final int _bufferSize;
|
||||
/** the lock */
|
||||
private final Object asyncBuffers = new Object();
|
||||
private final I2PAppContext _context;
|
||||
private final Log _log;
|
||||
private volatile boolean _isRunning;
|
||||
private Thread _refillThread;
|
||||
private final LinkedBlockingQueue<AsyncBuffer> _fullBuffers;
|
||||
private final LinkedBlockingQueue<AsyncBuffer> _emptyBuffers;
|
||||
private AsyncBuffer _currentBuffer;
|
||||
|
||||
private static final int STATUS_NEED_FILL = 0;
|
||||
private static final int STATUS_FILLING = 1;
|
||||
private static final int STATUS_FILLED = 2;
|
||||
private static final int STATUS_LIVE = 3;
|
||||
|
||||
public AsyncFortunaStandalone(I2PAppContext context) {
|
||||
super();
|
||||
_bufferCount = Math.max(context.getProperty("prng.buffers", DEFAULT_BUFFERS), 2);
|
||||
int bufferSize = Math.max(context.getProperty("prng.bufferSize", DEFAULT_BUFSIZE), 16*1024);
|
||||
asyncBuffers = new byte[_bufferCount][bufferSize];
|
||||
status = new int[_bufferCount];
|
||||
for (int i = 0; i < _bufferCount; i++)
|
||||
status[i] = STATUS_NEED_FILL;
|
||||
_bufferSize = Math.max(context.getProperty("prng.bufferSize", DEFAULT_BUFSIZE), 16*1024);
|
||||
_emptyBuffers = new LinkedBlockingQueue(_bufferCount);
|
||||
_fullBuffers = new LinkedBlockingQueue(_bufferCount);
|
||||
_context = context;
|
||||
context.statManager().createRequiredRateStat("prng.bufferWaitTime", "Delay for random number buffer (ms)", "Encryption", new long[] { 60*1000, 10*60*1000, 60*60*1000 } );
|
||||
context.statManager().createRequiredRateStat("prng.bufferFillTime", "Time to fill random number buffer (ms)", "Encryption", new long[] { 60*1000, 10*60*1000, 60*60*1000 } );
|
||||
@ -45,10 +50,27 @@ public class AsyncFortunaStandalone extends FortunaStandalone implements Runnabl
|
||||
}
|
||||
|
||||
public void startup() {
|
||||
Thread refillThread = new Thread(this, "PRNG");
|
||||
refillThread.setDaemon(true);
|
||||
refillThread.setPriority(Thread.MIN_PRIORITY+1);
|
||||
refillThread.start();
|
||||
for (int i = 0; i < _bufferCount; i++)
|
||||
_emptyBuffers.offer(new AsyncBuffer(_bufferSize));
|
||||
_isRunning = true;
|
||||
_refillThread = new Thread(this, "PRNG");
|
||||
_refillThread.setDaemon(true);
|
||||
_refillThread.setPriority(Thread.MIN_PRIORITY+1);
|
||||
_refillThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Note - methods may hang or NPE or throw IllegalStateExceptions after this
|
||||
* @since 0.8.8
|
||||
*/
|
||||
public void shutdown() {
|
||||
_isRunning = false;
|
||||
_emptyBuffers.clear();
|
||||
_fullBuffers.clear();
|
||||
_refillThread.interrupt();
|
||||
// unsynchronized to avoid hanging, may NPE elsewhere
|
||||
_currentBuffer = null;
|
||||
buffer = null;
|
||||
}
|
||||
|
||||
/** the seed is only propogated once the prng is started with startup() */
|
||||
@ -63,80 +85,67 @@ public class AsyncFortunaStandalone extends FortunaStandalone implements Runnabl
|
||||
@Override
|
||||
protected void allocBuffer() {}
|
||||
|
||||
private static class AsyncBuffer {
|
||||
public final byte[] buffer;
|
||||
|
||||
public AsyncBuffer(int size) {
|
||||
buffer = new byte[size];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* make the next available filled buffer current, scheduling any unfilled
|
||||
* buffers for refill, and blocking until at least one buffer is ready
|
||||
*/
|
||||
protected void rotateBuffer() {
|
||||
synchronized (asyncBuffers) {
|
||||
// wait until we get some filled
|
||||
AsyncBuffer old = _currentBuffer;
|
||||
if (old != null)
|
||||
_emptyBuffers.offer(old);
|
||||
long before = System.currentTimeMillis();
|
||||
long waited = 0;
|
||||
while (status[nextBuf] != STATUS_FILLED) {
|
||||
//System.out.println(Thread.currentThread().getName() + ": Next PRNG buffer "
|
||||
// + nextBuf + " isn't ready (" + status[nextBuf] + ")");
|
||||
//new Exception("source").printStackTrace();
|
||||
asyncBuffers.notifyAll();
|
||||
AsyncBuffer nextBuffer = null;
|
||||
|
||||
while (nextBuffer == null) {
|
||||
if (!_isRunning)
|
||||
throw new IllegalStateException("shutdown");
|
||||
try {
|
||||
asyncBuffers.wait();
|
||||
} catch (InterruptedException ie) {}
|
||||
waited = System.currentTimeMillis()-before;
|
||||
nextBuffer = _fullBuffers.take();
|
||||
} catch (InterruptedException ie) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
long waited = System.currentTimeMillis()-before;
|
||||
_context.statManager().addRateData("prng.bufferWaitTime", waited, 0);
|
||||
if (waited > 10*1000 && _log.shouldLog(Log.WARN))
|
||||
_log.warn(Thread.currentThread().getName() + ": Took " + waited
|
||||
+ "ms for a full PRNG buffer to be found");
|
||||
//System.out.println(Thread.currentThread().getName() + ": Switching to prng buffer " + nextBuf);
|
||||
buffer = asyncBuffers[nextBuf];
|
||||
status[nextBuf] = STATUS_LIVE;
|
||||
int prev=nextBuf-1;
|
||||
if (prev<0)
|
||||
prev = _bufferCount-1;
|
||||
if (status[prev] == STATUS_LIVE)
|
||||
status[prev] = STATUS_NEED_FILL;
|
||||
nextBuf++;
|
||||
if (nextBuf >= _bufferCount)
|
||||
nextBuf = 0;
|
||||
asyncBuffers.notify();
|
||||
_currentBuffer = nextBuffer;
|
||||
buffer = nextBuffer.buffer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The refiller thread
|
||||
*/
|
||||
public void run() {
|
||||
while (true) {
|
||||
int toFill = -1;
|
||||
while (_isRunning) {
|
||||
AsyncBuffer aBuff = null;
|
||||
try {
|
||||
synchronized (asyncBuffers) {
|
||||
for (int i = 0; i < _bufferCount; i++) {
|
||||
if (status[i] == STATUS_NEED_FILL) {
|
||||
status[i] = STATUS_FILLING;
|
||||
toFill = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (toFill == -1) {
|
||||
//System.out.println(Thread.currentThread().getName() + ": All pending buffers full");
|
||||
asyncBuffers.wait();
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException ie) {}
|
||||
aBuff = _emptyBuffers.take();
|
||||
} catch (InterruptedException ie) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (toFill != -1) {
|
||||
//System.out.println(Thread.currentThread().getName() + ": Filling prng buffer " + toFill);
|
||||
long before = System.currentTimeMillis();
|
||||
doFill(asyncBuffers[toFill]);
|
||||
doFill(aBuff.buffer);
|
||||
long after = System.currentTimeMillis();
|
||||
synchronized (asyncBuffers) {
|
||||
status[toFill] = STATUS_FILLED;
|
||||
//System.out.println(Thread.currentThread().getName() + ": Prng buffer " + toFill + " filled after " + (after-before));
|
||||
asyncBuffers.notifyAll();
|
||||
}
|
||||
_fullBuffers.offer(aBuff);
|
||||
_context.statManager().addRateData("prng.bufferFillTime", after - before, 0);
|
||||
Thread.yield();
|
||||
long waitTime = (after-before)*5;
|
||||
if (waitTime <= 0) // somehow postman saw waitTime show up as negative
|
||||
waitTime = 50;
|
||||
try { Thread.sleep(waitTime); } catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.i2p;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
@ -100,7 +101,7 @@ public class I2PAppContext {
|
||||
private volatile boolean _randomInitialized;
|
||||
private volatile boolean _keyGeneratorInitialized;
|
||||
protected volatile boolean _keyRingInitialized; // used in RouterContext
|
||||
private Set<Runnable> _shutdownTasks;
|
||||
protected final Set<Runnable> _shutdownTasks;
|
||||
private File _baseDir;
|
||||
private File _configDir;
|
||||
private File _routerDir;
|
||||
@ -114,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
|
||||
@ -164,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();
|
||||
@ -185,7 +194,7 @@ public class I2PAppContext {
|
||||
_elGamalAESEngineInitialized = false;
|
||||
_logManagerInitialized = false;
|
||||
_keyRingInitialized = false;
|
||||
_shutdownTasks = new ConcurrentHashSet(0);
|
||||
_shutdownTasks = new ConcurrentHashSet(32);
|
||||
initializeDirs();
|
||||
}
|
||||
|
||||
@ -843,12 +852,24 @@ public class I2PAppContext {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING - Shutdown tasks are not executed in an I2PAppContext.
|
||||
* You must be in a RouterContext for the tasks to be executed
|
||||
* at shutdown.
|
||||
* This method moved from Router in 0.7.1 so that clients
|
||||
* may use it without depending on router.jar.
|
||||
* @since 0.7.1
|
||||
*/
|
||||
public void addShutdownTask(Runnable task) {
|
||||
_shutdownTasks.add(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an unmodifiable Set
|
||||
* @since 0.7.1
|
||||
*/
|
||||
public Set<Runnable> getShutdownTasks() {
|
||||
return new HashSet(_shutdownTasks);
|
||||
return Collections.unmodifiableSet(_shutdownTasks);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,13 +47,14 @@ 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;
|
||||
private static final LinkedBlockingQueue<DHSessionKeyBuilder> _builders;
|
||||
private static final Thread _precalcThread;
|
||||
private static Thread _precalcThread;
|
||||
private static volatile boolean _isRunning;
|
||||
|
||||
// the data of importance
|
||||
private BigInteger _myPrivateValue;
|
||||
@ -96,14 +97,46 @@ 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);
|
||||
/**
|
||||
* Caller must synch on class
|
||||
* @since 0.8.8
|
||||
*/
|
||||
private static void startPrecalc() {
|
||||
_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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new DH key builder
|
||||
* or pulls a prebuilt one from the queue.
|
||||
@ -475,7 +508,7 @@ public class DHSessionKeyBuilder {
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while (true) {
|
||||
while (_isRunning) {
|
||||
|
||||
int curSize = 0;
|
||||
long start = System.currentTimeMillis();
|
||||
|
@ -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() {
|
||||
|
@ -42,8 +42,9 @@ class YKGenerator {
|
||||
private static final int MAX_NUM_BUILDERS;
|
||||
private static final int CALC_DELAY;
|
||||
private static final LinkedBlockingQueue<BigInteger[]> _values;
|
||||
private static final Thread _precalcThread;
|
||||
private static final I2PAppContext ctx;
|
||||
private static Thread _precalcThread;
|
||||
private static 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";
|
||||
@ -75,16 +76,47 @@ class YKGenerator {
|
||||
// _log.debug("ElGamal YK Precalc (minimum: " + MIN_NUM_BUILDERS + " max: " + MAX_NUM_BUILDERS + ", delay: "
|
||||
// + CALC_DELAY + ")");
|
||||
|
||||
startPrecalc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Caller must synch on class
|
||||
* @since 0.8.8
|
||||
*/
|
||||
private static void startPrecalc() {
|
||||
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));
|
||||
_precalcThread.setName("YK Precalc");
|
||||
_precalcThread.setDaemon(true);
|
||||
_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() {
|
||||
return _values.size();
|
||||
}
|
||||
@ -161,7 +193,7 @@ class YKGenerator {
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while (true) {
|
||||
while (_isRunning) {
|
||||
int curSize = 0;
|
||||
//long start = Clock.getInstance().now();
|
||||
int startSize = getSize();
|
||||
@ -172,7 +204,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;
|
||||
|
@ -83,6 +83,18 @@ public class SDSCache<V extends SimpleDataStructure> {
|
||||
_log.debug("New SDSCache for " + rvClass + " data size: " + len +
|
||||
" max: " + size + " max mem: " + (len * size));
|
||||
I2PAppContext.getGlobalContext().statManager().createRateStat(_statName, "Hit rate", "Router", new long[] { 10*60*1000 });
|
||||
I2PAppContext.getGlobalContext().addShutdownTask(new Shutdown());
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.8.8
|
||||
*/
|
||||
private class Shutdown implements Runnable {
|
||||
public void run() {
|
||||
synchronized(_cache) {
|
||||
_cache.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,6 +29,8 @@ public class Timestamper implements Runnable {
|
||||
private boolean _daemon;
|
||||
private boolean _initialized;
|
||||
private boolean _wellSynced;
|
||||
private volatile boolean _isRunning;
|
||||
private Thread _timestamperThread;
|
||||
|
||||
private static final int MIN_QUERY_FREQUENCY = 5*60*1000;
|
||||
private static final int DEFAULT_QUERY_FREQUENCY = 5*60*1000;
|
||||
@ -106,10 +108,11 @@ public class Timestamper implements Runnable {
|
||||
}
|
||||
|
||||
private void startTimestamper() {
|
||||
I2PThread t = new I2PThread(this, "Timestamper");
|
||||
t.setPriority(I2PThread.MIN_PRIORITY);
|
||||
t.setDaemon(_daemon);
|
||||
t.start();
|
||||
_timestamperThread = new I2PThread(this, "Timestamper", _daemon);
|
||||
_timestamperThread.setPriority(I2PThread.MIN_PRIORITY);
|
||||
_isRunning = true;
|
||||
_timestamperThread.start();
|
||||
_context.addShutdownTask(new Shutdown());
|
||||
}
|
||||
|
||||
public void waitForInitialization() {
|
||||
@ -121,6 +124,15 @@ public class Timestamper implements Runnable {
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
|
||||
/** @since 0.8.8 */
|
||||
private class Shutdown implements Runnable {
|
||||
public void run() {
|
||||
_isRunning = false;
|
||||
if (_timestamperThread != null)
|
||||
_timestamperThread.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try { Thread.sleep(1000); } catch (InterruptedException ie) {}
|
||||
_log = _context.logManager().getLog(Timestamper.class);
|
||||
@ -128,7 +140,7 @@ public class Timestamper implements Runnable {
|
||||
_log.info("Starting timestamper");
|
||||
boolean lastFailed = false;
|
||||
try {
|
||||
while (true) {
|
||||
while (_isRunning) {
|
||||
updateConfig();
|
||||
if (!_disabled) {
|
||||
// first the servers for our country, if we know what country we're in...
|
||||
|
@ -107,8 +107,18 @@ public class DecayingBloomFilter {
|
||||
context.statManager().createRateStat("router.decayingBloomFilter." + name + ".log10(falsePos)",
|
||||
"log10 of the false positive rate (must have net.i2p.util.DecayingBloomFilter=DEBUG)",
|
||||
"Router", new long[] { Math.max(60*1000, durationMs) });
|
||||
context.addShutdownTask(new Shutdown());
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.8.8
|
||||
*/
|
||||
private class Shutdown implements Runnable {
|
||||
public void run() {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
public long getCurrentDuplicateCount() { return _currentDuplicates; }
|
||||
|
||||
public int getInsertedCount() {
|
||||
|
@ -93,8 +93,18 @@ public class DecayingHashSet extends DecayingBloomFilter {
|
||||
"Size", "Router", new long[] { Math.max(60*1000, durationMs) });
|
||||
context.statManager().createRateStat("router.decayingHashSet." + name + ".dups",
|
||||
"1000000 * Duplicates/Size", "Router", new long[] { Math.max(60*1000, durationMs) });
|
||||
context.addShutdownTask(new Shutdown());
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.8.8
|
||||
*/
|
||||
private class Shutdown implements Runnable {
|
||||
public void run() {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
/** unsynchronized but only used for logging elsewhere */
|
||||
@Override
|
||||
public int getInsertedCount() {
|
||||
|
@ -55,6 +55,8 @@ public class EepGet {
|
||||
protected long _bytesTransferred;
|
||||
protected long _bytesRemaining;
|
||||
protected int _currentAttempt;
|
||||
protected int _responseCode = -1;
|
||||
protected boolean _shouldWriteErrorToOutput;
|
||||
protected String _etag;
|
||||
protected String _lastModified;
|
||||
protected boolean _encodingChunked;
|
||||
@ -245,7 +247,14 @@ public class EepGet {
|
||||
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified);
|
||||
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause);
|
||||
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt);
|
||||
|
||||
/**
|
||||
* Note: Headers are not processed, and this is not called, for most error response codes,
|
||||
* unless setWriteErrorToOutput() is called before fetch().
|
||||
* To be changed?
|
||||
*/
|
||||
public void headerReceived(String url, int currentAttempt, String key, String val);
|
||||
|
||||
public void attempting(String url);
|
||||
}
|
||||
protected class CLIStatusListener implements StatusListener {
|
||||
@ -560,7 +569,7 @@ public class EepGet {
|
||||
throw new IOException("HTTP response size " + _bytesRemaining + " violates maximum of " + _maxSize + " bytes");
|
||||
|
||||
int remaining = (int)_bytesRemaining;
|
||||
byte buf[] = new byte[1024];
|
||||
byte buf[] = new byte[8*1024];
|
||||
while (_keepFetching && ( (remaining > 0) || !strictSize ) && !_aborted) {
|
||||
int toRead = buf.length;
|
||||
if (strictSize && toRead > remaining)
|
||||
@ -654,13 +663,13 @@ public class EepGet {
|
||||
|
||||
boolean read = DataHelper.readLine(_proxyIn, buf);
|
||||
if (!read) throw new IOException("Unable to read the first line");
|
||||
int responseCode = handleStatus(buf.toString());
|
||||
_responseCode = handleStatus(buf.toString());
|
||||
boolean redirect = false;
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("rc: " + responseCode + " for " + _actualURL);
|
||||
_log.debug("rc: " + _responseCode + " for " + _actualURL);
|
||||
boolean rcOk = false;
|
||||
switch (responseCode) {
|
||||
switch (_responseCode) {
|
||||
case 200: // full
|
||||
if (_outputStream != null)
|
||||
_out = _outputStream;
|
||||
@ -693,20 +702,51 @@ public class EepGet {
|
||||
case 404: // not found
|
||||
case 409: // bad addr helper
|
||||
case 503: // no outproxy
|
||||
_keepFetching = false;
|
||||
_transferFailed = true;
|
||||
// maybe we should throw instead of return to get the return code back to the user
|
||||
return;
|
||||
if (_alreadyTransferred == 0 && !_shouldWriteErrorToOutput) {
|
||||
_keepFetching = false;
|
||||
return;
|
||||
}
|
||||
// output the error data to the stream
|
||||
rcOk = true;
|
||||
if (_out == null) {
|
||||
if (_outputStream != null)
|
||||
_out = _outputStream;
|
||||
else
|
||||
_out = new FileOutputStream(_outputFile, true);
|
||||
}
|
||||
break;
|
||||
case 416: // completed (or range out of reach)
|
||||
_bytesRemaining = 0;
|
||||
_keepFetching = false;
|
||||
return;
|
||||
if (_alreadyTransferred == 0 && !_shouldWriteErrorToOutput) {
|
||||
_keepFetching = false;
|
||||
return;
|
||||
}
|
||||
// output the error data to the stream
|
||||
rcOk = true;
|
||||
if (_out == null) {
|
||||
if (_outputStream != null)
|
||||
_out = _outputStream;
|
||||
else
|
||||
_out = new FileOutputStream(_outputFile, true);
|
||||
}
|
||||
break;
|
||||
case 504: // gateway timeout
|
||||
// throw out of doFetch() to fetch() and try again
|
||||
throw new IOException("HTTP Proxy timeout");
|
||||
default:
|
||||
rcOk = false;
|
||||
_keepFetching = false;
|
||||
if (_alreadyTransferred == 0 && !_shouldWriteErrorToOutput) {
|
||||
_keepFetching = false;
|
||||
} else {
|
||||
// output the error data to the stream
|
||||
rcOk = true;
|
||||
if (_out == null) {
|
||||
if (_outputStream != null)
|
||||
_out = _outputStream;
|
||||
else
|
||||
_out = new FileOutputStream(_outputFile, true);
|
||||
}
|
||||
}
|
||||
_transferFailed = true;
|
||||
}
|
||||
|
||||
@ -742,7 +782,7 @@ public class EepGet {
|
||||
increment(lookahead, cur);
|
||||
if (isEndOfHeaders(lookahead)) {
|
||||
if (!rcOk)
|
||||
throw new IOException("Invalid HTTP response code: " + responseCode);
|
||||
throw new IOException("Invalid HTTP response code: " + _responseCode);
|
||||
if (_encodingChunked) {
|
||||
_bytesRemaining = readChunkLength();
|
||||
}
|
||||
@ -842,7 +882,7 @@ public class EepGet {
|
||||
if (val.indexOf("chunked") != -1)
|
||||
_encodingChunked = true;
|
||||
} else if (key.equalsIgnoreCase("Content-Type")) {
|
||||
_contentType=val;
|
||||
_contentType=val.trim();
|
||||
} else if (key.equalsIgnoreCase("Location")) {
|
||||
_redirectLocation=val.trim();
|
||||
} else {
|
||||
@ -991,4 +1031,34 @@ public class EepGet {
|
||||
public String getContentType() {
|
||||
return _contentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* The server response (200, etc).
|
||||
* @return -1 if invalid, or if the proxy never responded,
|
||||
* or if no proxy was used and the server never responded.
|
||||
* If a non-proxied request partially succeeded (for example a redirect followed
|
||||
* by a fail, or a partial fetch followed by a fail), this will
|
||||
* be the last status code received.
|
||||
* Note that fetch() may return false even if this returns 200.
|
||||
*
|
||||
* @since 0.8.8
|
||||
*/
|
||||
public int getStatusCode() {
|
||||
return _responseCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* If called (before calling fetch()),
|
||||
* data from the server or proxy will be written to the
|
||||
* output file or stream even on an error response code (4xx, 5xx, etc).
|
||||
* The error data will only be written if no previous data was written
|
||||
* on an earlier try.
|
||||
* Caller must of course check getStatusCode() or the
|
||||
* fetch() return value.
|
||||
*
|
||||
* @since 0.8.8
|
||||
*/
|
||||
public void setWriteErrorToOutput() {
|
||||
_shouldWriteErrorToOutput = true;
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,14 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste
|
||||
_haveNextGaussian = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note - methods may hang or NPE or throw IllegalStateExceptions after this
|
||||
* @since 0.8.8
|
||||
*/
|
||||
public void shutdown() {
|
||||
_fortuna.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setSeed(byte buf[]) {
|
||||
_fortuna.addRandomBytes(buf);
|
||||
|
@ -20,7 +20,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
*
|
||||
*/
|
||||
public class I2PThread extends Thread {
|
||||
private static volatile Log _log;
|
||||
/**
|
||||
* Non-static to avoid refs to old context in Android.
|
||||
* Probably should just remove all the logging though.
|
||||
*/
|
||||
private volatile Log _log;
|
||||
private static final Set _listeners = new CopyOnWriteArraySet();
|
||||
private String _name;
|
||||
private Exception _createdBy;
|
||||
@ -61,8 +65,9 @@ public class I2PThread extends Thread {
|
||||
_createdBy = new Exception("Created by");
|
||||
}
|
||||
|
||||
private static void log(int level, String msg) { log(level, msg, null); }
|
||||
private static void log(int level, String msg, Throwable t) {
|
||||
private void log(int level, String msg) { log(level, msg, null); }
|
||||
|
||||
private void log(int level, String msg, Throwable t) {
|
||||
// we cant assume log is created
|
||||
if (_log == null) _log = new Log(I2PThread.class);
|
||||
if (_log.shouldLog(level))
|
||||
@ -85,7 +90,9 @@ public class I2PThread extends Thread {
|
||||
if (t instanceof OutOfMemoryError)
|
||||
fireOOM((OutOfMemoryError)t);
|
||||
}
|
||||
log(Log.INFO, "Thread finished normally: " + _name);
|
||||
// This creates a new I2PAppContext after it was deleted
|
||||
// in Router.finalShutdown() via RouterContext.killGlobalContext()
|
||||
//log(Log.INFO, "Thread finished normally: " + _name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2,61 +2,79 @@ package net.i2p.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Offer a glimpse into the last few console messages generated
|
||||
*
|
||||
* Offer a glimpse into the last few console messages generated.
|
||||
* Maintains two buffers, one normal and one critical.
|
||||
*/
|
||||
public class LogConsoleBuffer {
|
||||
private I2PAppContext _context;
|
||||
private final List<String> _buffer;
|
||||
private final List<String> _critBuffer;
|
||||
private final int lim;
|
||||
private final LinkedBlockingQueue<String> _buffer;
|
||||
private final LinkedBlockingQueue<String> _critBuffer;
|
||||
|
||||
/**
|
||||
* Uses default limit from LogManager.
|
||||
* As of 0.8.8, limit is not checked at runtime.
|
||||
*
|
||||
* @param context unused
|
||||
*/
|
||||
public LogConsoleBuffer(I2PAppContext context) {
|
||||
_context = context;
|
||||
_buffer = new ArrayList();
|
||||
_critBuffer = new ArrayList();
|
||||
this(LogManager.DEFAULT_CONSOLEBUFFERSIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param limit max size of each buffer
|
||||
* In theory the limit is configurable, but it isn't in the UI,
|
||||
* so set it at construction.
|
||||
*
|
||||
* @since 0.8.8
|
||||
*/
|
||||
public LogConsoleBuffer(int limit) {
|
||||
lim = Math.max(limit, 4);
|
||||
// Add some extra room to minimize the chance of losing a message,
|
||||
// since we are doing offer() below.
|
||||
_buffer = new LinkedBlockingQueue(limit + 4);
|
||||
_critBuffer = new LinkedBlockingQueue(limit + 4);
|
||||
}
|
||||
|
||||
void add(String msg) {
|
||||
int lim = _context.logManager().getConsoleBufferSize();
|
||||
synchronized (_buffer) {
|
||||
while (_buffer.size() >= lim)
|
||||
_buffer.remove(0);
|
||||
_buffer.add(msg);
|
||||
}
|
||||
}
|
||||
void addCritical(String msg) {
|
||||
int lim = _context.logManager().getConsoleBufferSize();
|
||||
synchronized (_critBuffer) {
|
||||
while (_critBuffer.size() >= lim)
|
||||
_critBuffer.remove(0);
|
||||
_critBuffer.add(msg);
|
||||
}
|
||||
_buffer.poll();
|
||||
_buffer.offer(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the currently bufferd messages, earlier values were generated...
|
||||
* Only adds to the critical buffer, not to both.
|
||||
*
|
||||
*/
|
||||
void addCritical(String msg) {
|
||||
while (_critBuffer.size() >= lim)
|
||||
_critBuffer.poll();
|
||||
_critBuffer.offer(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the currently buffered messages, earlier values were generated...
|
||||
* earlier. All values are strings with no formatting (as they are written
|
||||
* in the logs)
|
||||
*
|
||||
* @return oldest first
|
||||
*/
|
||||
public List<String> getMostRecentMessages() {
|
||||
synchronized (_buffer) {
|
||||
return new ArrayList(_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the currently bufferd crutucak messages, earlier values were generated...
|
||||
* Retrieve the currently buffered critical messages, earlier values were generated...
|
||||
* earlier. All values are strings with no formatting (as they are written
|
||||
* in the logs)
|
||||
*
|
||||
* @return oldest first
|
||||
*/
|
||||
public List<String> getMostRecentCriticalMessages() {
|
||||
synchronized (_critBuffer) {
|
||||
return new ArrayList(_critBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ public class LogManager {
|
||||
_log = getLog(LogManager.class);
|
||||
String location = context.getProperty(CONFIG_LOCATION_PROP, CONFIG_LOCATION_DEFAULT);
|
||||
setConfig(location);
|
||||
_consoleBuffer = new LogConsoleBuffer(context);
|
||||
_consoleBuffer = new LogConsoleBuffer(_consoleBufferSize);
|
||||
// If we aren't in the router context, delay creating the LogWriter until required,
|
||||
// so it doesn't create a log directory and log files unless there is output.
|
||||
// In the router context, we have to rotate to a new log file at startup or the logs.jsp
|
||||
@ -656,6 +656,9 @@ public class LogManager {
|
||||
// this could generate out-of-order messages
|
||||
_writer.flushRecords(false);
|
||||
_writer.stopWriting();
|
||||
synchronized (_writer) {
|
||||
_writer.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ class LogWriter implements Runnable {
|
||||
private File _currentFile;
|
||||
private final LogManager _manager;
|
||||
|
||||
private boolean _write;
|
||||
private volatile boolean _write;
|
||||
private static final int MAX_DISKFULL_MESSAGES = 8;
|
||||
private int _diskFullMessageCount;
|
||||
|
||||
@ -55,7 +55,8 @@ class LogWriter implements Runnable {
|
||||
rotateFile();
|
||||
while (_write) {
|
||||
flushRecords();
|
||||
rereadConfig();
|
||||
if (_write)
|
||||
rereadConfig();
|
||||
}
|
||||
//System.err.println("Done writing");
|
||||
} catch (Exception e) {
|
||||
|
@ -28,6 +28,12 @@ public class RandomSource extends SecureRandom implements EntropyHarvester {
|
||||
private final EntropyHarvester _entropyHarvester;
|
||||
protected final I2PAppContext _context;
|
||||
|
||||
/**
|
||||
* Deprecated - do not instantiate this directly, as you won't get the
|
||||
* good one (Fortuna). Use getInstance() or
|
||||
* I2PAppContext.getGlobalContext().random() to get the FortunaRandomSource
|
||||
* instance.
|
||||
*/
|
||||
public RandomSource(I2PAppContext context) {
|
||||
super();
|
||||
_context = context;
|
||||
@ -202,10 +208,4 @@ public class RandomSource extends SecureRandom implements EntropyHarvester {
|
||||
rs.saveSeed();
|
||||
}
|
||||
}
|
||||
|
||||
// noop
|
||||
private static class DummyEntropyHarvester implements EntropyHarvester {
|
||||
public void feedEntropy(String source, long data, int bitoffset, int bits) {}
|
||||
public void feedEntropy(String source, byte[] data, int offset, int len) {}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package net.i2p.util;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
@ -48,12 +49,25 @@ public class SimpleScheduler {
|
||||
_threads = (int) Math.max(MIN_THREADS, Math.min(MAX_THREADS, 1 + (maxMemory / (32*1024*1024))));
|
||||
_executor = new ScheduledThreadPoolExecutor(_threads, new CustomThreadFactory());
|
||||
_executor.prestartAllCoreThreads();
|
||||
// don't bother saving ref to remove hook if somebody else calls stop
|
||||
_context.addShutdownTask(new Shutdown());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the SimpleScheduler.
|
||||
* @since 0.8.8
|
||||
*/
|
||||
private class Shutdown implements Runnable {
|
||||
public void run() {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the SimpleScheduler.
|
||||
* Subsequent executions should not throw a RejectedExecutionException.
|
||||
*/
|
||||
public void stop() {
|
||||
_executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
|
||||
_executor.shutdownNow();
|
||||
}
|
||||
|
||||
|
@ -53,16 +53,32 @@ public class SimpleTimer {
|
||||
executor.setDaemon(true);
|
||||
executor.start();
|
||||
}
|
||||
_context.addShutdownTask(new Shutdown());
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.8.8
|
||||
*/
|
||||
private class Shutdown implements Runnable {
|
||||
public void run() {
|
||||
removeSimpleTimer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the SimpleTimer.
|
||||
*/
|
||||
public void removeSimpleTimer() {
|
||||
synchronized(_events) {
|
||||
runn.setAnswer(false);
|
||||
_events.clear();
|
||||
_eventTimes.clear();
|
||||
_events.notifyAll();
|
||||
}
|
||||
synchronized (_readyEvents) {
|
||||
_readyEvents.clear();
|
||||
_readyEvents.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3,6 +3,7 @@ package net.i2p.util;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
@ -48,12 +49,25 @@ public class SimpleTimer2 {
|
||||
_threads = (int) Math.max(MIN_THREADS, Math.min(MAX_THREADS, 1 + (maxMemory / (32*1024*1024))));
|
||||
_executor = new CustomScheduledThreadPoolExecutor(_threads, new CustomThreadFactory());
|
||||
_executor.prestartAllCoreThreads();
|
||||
// don't bother saving ref to remove hook if somebody else calls stop
|
||||
_context.addShutdownTask(new Shutdown());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the SimpleTimer.
|
||||
* @since 0.8.8
|
||||
*/
|
||||
private class Shutdown implements Runnable {
|
||||
public void run() {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the SimpleTimer.
|
||||
* Subsequent executions should not throw a RejectedExecutionException.
|
||||
*/
|
||||
public void stop() {
|
||||
_executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
|
||||
_executor.shutdownNow();
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user