propagate from branch 'i2p.i2p.zzz.android' (head cbf2d39e1944b9d601558761d0eedcdebfd2f589)

to branch 'i2p.i2p' (head c2393e50afccfd5682a9086f0eec2a0700cda2c9)
This commit is contained in:
zzz
2011-06-30 12:27:00 +00:00
53 changed files with 850 additions and 215 deletions

View File

@ -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) {}
}
}
}

View File

@ -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);
}
/**

View File

@ -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();

View File

@ -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() {

View File

@ -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;

View File

@ -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();
}
}
}
/**

View File

@ -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...

View File

@ -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() {

View File

@ -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() {

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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();
}
}
}

View File

@ -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) {

View File

@ -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) {}
}
}

View File

@ -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();
}

View File

@ -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();
}
}
/**

View File

@ -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();
}