diff --git a/history.txt b/history.txt index 52bec45356..61284c2651 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,8 @@ +2015-01-07 zzz + * ClientAppConfig: Start i2ptunnel sooner + * Router: Add startup/shutdown state machine + * Tunnels: Cleanup, catch more cases of zero-hop configuration + 2015-01-05 zzz * Blocklist: - Rewrite to read and merge multiple files diff --git a/installer/resources/clients.config b/installer/resources/clients.config index bb370c47e0..bcc0556207 100644 --- a/installer/resources/clients.config +++ b/installer/resources/clients.config @@ -39,6 +39,7 @@ clientApp.1.startOnLoad=false clientApp.2.main=net.i2p.i2ptunnel.TunnelControllerGroup clientApp.2.name=Application tunnels clientApp.2.args=i2ptunnel.config +clientApp.2.delay=35 clientApp.2.startOnLoad=true # run our own eepsite with a seperate jetty instance diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java index ab8e7c38b8..b390cbcc06 100644 --- a/router/java/src/net/i2p/router/Router.java +++ b/router/java/src/net/i2p/router/Router.java @@ -78,16 +78,15 @@ public class Router implements RouterClock.ClockShiftListener { private boolean _higherVersionSeen; //private SessionKeyPersistenceHelper _sessionKeyPersistenceHelper; private boolean _killVMOnEnd; - private volatile boolean _isAlive; private int _gracefulExitCode; private I2PThread.OOMEventListener _oomListener; private ShutdownHook _shutdownHook; - /** non-cancellable shutdown has begun */ - private volatile boolean _shutdownInProgress; private I2PThread _gracefulShutdownDetector; private RouterWatchdog _watchdog; private Thread _watchdogThread; private final EventLog _eventLog; + private final Object _stateLock = new Object(); + private State _state = State.UNINITIALIZED; public final static String PROP_CONFIG_FILE = "router.configLocation"; @@ -105,7 +104,8 @@ public class Router implements RouterClock.ClockShiftListener { /** this does not put an 'H' in your routerInfo **/ public final static String PROP_HIDDEN_HIDDEN = "router.isHidden"; public final static String PROP_DYNAMIC_KEYS = "router.dynamicKeys"; - public final static String PROP_SHUTDOWN_IN_PROGRESS = "__shutdownInProgress"; + /** deprecated, use gracefulShutdownInProgress() */ + private final static String PROP_SHUTDOWN_IN_PROGRESS = "__shutdownInProgress"; private static final String PROP_IB_RANDOM_KEY = TunnelPoolSettings.PREFIX_INBOUND_EXPLORATORY + TunnelPoolSettings.PROP_RANDOM_KEY; private static final String PROP_OB_RANDOM_KEY = TunnelPoolSettings.PREFIX_OUTBOUND_EXPLORATORY + TunnelPoolSettings.PROP_RANDOM_KEY; public final static String DNS_CACHE_TIME = "" + (5*60); @@ -287,6 +287,7 @@ public class Router implements RouterClock.ClockShiftListener { _config.put("router.previousVersion", RouterVersion.VERSION); saveConfig(); } + changeState(State.INITIALIZED); // ********* Start no threads before here ********* // } @@ -459,11 +460,16 @@ public class Router implements RouterClock.ClockShiftListener { * Standard standalone installation uses main() instead, which * checks for updates and then calls this. * + * This may take quite a while, especially if NTP fails. + * * @since public as of 0.9 for Android and other embedded uses */ public synchronized void runRouter() { - if (_isAlive) - throw new IllegalStateException(); + synchronized(_stateLock) { + if (_state != State.INITIALIZED) + throw new IllegalStateException(); + changeState(State.STARTING_1); + } String last = _config.get("router.previousFullVersion"); if (last != null) { _eventLog.addEvent(EventLog.UPDATED, "from " + last + " to " + RouterVersion.FULL_VERSION); @@ -471,7 +477,7 @@ public class Router implements RouterClock.ClockShiftListener { } _eventLog.addEvent(EventLog.STARTED, RouterVersion.FULL_VERSION); startupStuff(); - _isAlive = true; + changeState(State.STARTING_2); _started = _context.clock().now(); try { Runtime.getRuntime().addShutdownHook(_shutdownHook); @@ -518,6 +524,7 @@ public class Router implements RouterClock.ClockShiftListener { if (_log.shouldLog(Log.INFO)) _log.info("Waited " + waited + "ms to initialize"); + changeState(State.STARTING_3); _context.jobQueue().addJob(new StartupJob(_context)); } @@ -573,9 +580,141 @@ public class Router implements RouterClock.ClockShiftListener { } return props; } + + ////////// begin state management - public boolean isAlive() { return _isAlive; } + /** + * Startup / shutdown states + * + * @since 0.9.18 + */ + private enum State { + UNINITIALIZED, + /** constructor complete */ + INITIALIZED, + /** runRouter() called */ + STARTING_1, + /** startupStuff() complete, most of the time here is NTP */ + STARTING_2, + /** NTP done, Job queue started, StartupJob queued, runRouter() returned */ + STARTING_3, + /** RIs loaded. From STARTING_3 */ + NETDB_READY, + /** Non-zero-hop expl. tunnels built. From STARTING_3 */ + EXPL_TUNNELS_READY, + /** from NETDB_READY or EXPL_TUNNELS_READY */ + RUNNING, + /** + * A "soft" restart, primarily of the comm system, after + * a port change or large step-change in system time. + * Does not stop the whole JVM, so it is safe even in the absence + * of the wrapper. + * This is not a graceful restart - all peer connections are dropped immediately. + */ + RESTARTING, + /** cancellable shutdown has begun */ + GRACEFUL_SHUTDOWN, + /** In shutdown(). Non-cancellable shutdown has begun */ + FINAL_SHUTDOWN_1, + /** In shutdown2(). Killing everything */ + FINAL_SHUTDOWN_2, + /** In finalShutdown(). Final cleanup */ + FINAL_SHUTDOWN_3, + /** all done */ + STOPPED + } + /** + * @since 0.9.18 + */ + private void changeState(State state) { + State oldState; + synchronized(_stateLock) { + oldState = _state; + _state = state; + } + if (_log != null && state != State.STOPPED && _log.shouldLog(Log.WARN)) + _log.warn("Router state change from " + oldState + " to " + state /* , new Exception() */ ); + } + + /** + * True during the initial start, but false during a soft restart. + */ + public boolean isAlive() { + synchronized(_stateLock) { + return _state == State.RUNNING || + _state == State.GRACEFUL_SHUTDOWN || + _state == State.STARTING_1 || + _state == State.STARTING_2 || + _state == State.STARTING_3 || + _state == State.NETDB_READY || + _state == State.EXPL_TUNNELS_READY; + } + } + + /** + * Only for Restarter, after soft restart is complete + * @since 0.8.12 + */ + public void setIsAlive() { + changeState(State.RUNNING); + } + + /** + * Only for NetDB, after RIs are loaded + * @since 0.9.18 + */ + public void setNetDbReady() { + synchronized(_stateLock) { + if (_state == State.STARTING_3) + changeState(State.NETDB_READY); + else if (_state == State.EXPL_TUNNELS_READY) + changeState(State.RUNNING); + } + } + + /** + * Only for Tunnel Building, after we have non-zero-hop expl. tunnels + * @since 0.9.18 + */ + public void setExplTunnelsReady() { + synchronized(_stateLock) { + if (_state == State.STARTING_3) + changeState(State.EXPL_TUNNELS_READY); + else if (_state == State.NETDB_READY) + changeState(State.RUNNING); + } + } + + /** + * Is a graceful shutdown in progress? This may be cancelled. + * Note that this also returns true if an uncancellable final shutdown is in progress. + */ + public boolean gracefulShutdownInProgress() { + synchronized(_stateLock) { + return _state == State.GRACEFUL_SHUTDOWN || + _state == State.FINAL_SHUTDOWN_1 || + _state == State.FINAL_SHUTDOWN_2 || + _state == State.FINAL_SHUTDOWN_3 || + _state == State.STOPPED; + } + } + + /** + * Is a final shutdown in progress? This may not be cancelled. + * @since 0.8.12 + */ + public boolean isFinalShutdownInProgress() { + synchronized(_stateLock) { + return _state == State.FINAL_SHUTDOWN_1 || + _state == State.FINAL_SHUTDOWN_2 || + _state == State.FINAL_SHUTDOWN_3 || + _state == State.STOPPED; + } + } + + ////////// end state management + /** * Rebuild and republish our routerInfo since something significant * has changed. @@ -801,9 +940,14 @@ public class Router implements RouterClock.ClockShiftListener { * Shutdown with no chance of cancellation */ public synchronized void shutdown(int exitCode) { - if (_shutdownInProgress) - return; - _shutdownInProgress = true; + synchronized(_stateLock) { + if (_state == State.FINAL_SHUTDOWN_1 || + _state == State.FINAL_SHUTDOWN_2 || + _state == State.FINAL_SHUTDOWN_3 || + _state == State.STOPPED) + return; + changeState(State.FINAL_SHUTDOWN_1); + } _context.throttle().setShutdownStatus(); if (_shutdownHook != null) { try { @@ -819,10 +963,10 @@ public class Router implements RouterClock.ClockShiftListener { * NOT to be called by others, use shutdown(). */ public synchronized void shutdown2(int exitCode) { + changeState(State.FINAL_SHUTDOWN_2); // help us shut down esp. after OOM int priority = (exitCode == EXIT_OOM) ? Thread.MAX_PRIORITY - 1 : Thread.NORM_PRIORITY + 2; Thread.currentThread().setPriority(priority); - _shutdownInProgress = true; _log.log(Log.CRIT, "Starting final shutdown(" + exitCode + ')'); // So we can get all the way to the end // No, you can't do Thread.currentThread.setDaemon(false) @@ -832,7 +976,6 @@ public class Router implements RouterClock.ClockShiftListener { } catch (Throwable t) {} } ((RouterClock) _context.clock()).removeShiftListener(this); - _isAlive = false; _context.random().saveSeed(); I2PThread.removeOOMEventListener(_oomListener); // Run the shutdown hooks first in case they want to send some goodbye messages @@ -916,6 +1059,7 @@ public class Router implements RouterClock.ClockShiftListener { * Cancel the JVM runtime hook before calling this. */ private synchronized void finalShutdown(int exitCode) { + changeState(State.FINAL_SHUTDOWN_3); clearCaches(); _log.log(Log.CRIT, "Shutdown(" + exitCode + ") complete" /* , new Exception("Shutdown") */ ); try { _context.logManager().shutdown(); } catch (Throwable t) { } @@ -948,6 +1092,7 @@ public class Router implements RouterClock.ClockShiftListener { } else if (SystemVersion.isAndroid()) { Runtime.getRuntime().gc(); } + changeState(State.STOPPED); } /** @@ -962,13 +1107,21 @@ public class Router implements RouterClock.ClockShiftListener { public void shutdownGracefully() { shutdownGracefully(EXIT_GRACEFUL); } + /** * Call this with EXIT_HARD or EXIT_HARD_RESTART for a non-blocking, * hard, non-graceful shutdown with a brief delay to allow a UI response + * + * Returns silently if a final shutdown is already in progress. */ public void shutdownGracefully(int exitCode) { + synchronized(_stateLock) { + if (isFinalShutdownInProgress()) + return; // too late + changeState(State.GRACEFUL_SHUTDOWN); + } _gracefulExitCode = exitCode; - _config.put(PROP_SHUTDOWN_IN_PROGRESS, "true"); + //_config.put(PROP_SHUTDOWN_IN_PROGRESS, "true"); _context.throttle().setShutdownStatus(); synchronized (_gracefulShutdownDetector) { _gracefulShutdownDetector.notifyAll(); @@ -978,10 +1131,16 @@ public class Router implements RouterClock.ClockShiftListener { /** * Cancel any prior request to shut the router down gracefully. * + * Returns silently if a final shutdown is already in progress. */ public void cancelGracefulShutdown() { + synchronized(_stateLock) { + if (isFinalShutdownInProgress()) + return; // too late + changeState(State.RUNNING); + } _gracefulExitCode = -1; - _config.remove(PROP_SHUTDOWN_IN_PROGRESS); + //_config.remove(PROP_SHUTDOWN_IN_PROGRESS); _context.throttle().cancelShutdownStatus(); synchronized (_gracefulShutdownDetector) { _gracefulShutdownDetector.notifyAll(); @@ -993,21 +1152,6 @@ public class Router implements RouterClock.ClockShiftListener { */ public int scheduledGracefulExitCode() { return _gracefulExitCode; } - /** - * Is a graceful shutdown in progress? This may be cancelled. - */ - public boolean gracefulShutdownInProgress() { - return (null != _config.get(PROP_SHUTDOWN_IN_PROGRESS)); - } - - /** - * Is a final shutdown in progress? This may not be cancelled. - * @since 0.8.12 - */ - public boolean isFinalShutdownInProgress() { - return _shutdownInProgress; - } - /** How long until the graceful shutdown will kill us? */ public long getShutdownTimeRemaining() { if (_gracefulExitCode <= 0) return -1; // maybe Long.MAX_VALUE would be better? @@ -1094,8 +1238,10 @@ public class Router implements RouterClock.ClockShiftListener { * @since 0.8.8 */ public void clockShift(long delta) { - if (gracefulShutdownInProgress() || !_isAlive) - return; + synchronized(_stateLock) { + if (gracefulShutdownInProgress() || !isAlive()) + return; + } if (delta > -60*1000 && delta < 60*1000) return; _eventLog.addEvent(EventLog.CLOCK_SHIFT, Long.toString(delta)); @@ -1121,24 +1267,18 @@ public class Router implements RouterClock.ClockShiftListener { * Poll isAlive() if you need to know when the restart is complete. */ public synchronized void restart() { - if (gracefulShutdownInProgress() || !_isAlive) - return; + synchronized(_stateLock) { + if (gracefulShutdownInProgress() || !isAlive()) + return; + changeState(State.RESTARTING); + } ((RouterClock) _context.clock()).removeShiftListener(this); - _isAlive = false; _started = _context.clock().now(); Thread t = new Thread(new Restarter(_context), "Router Restart"); t.setPriority(Thread.NORM_PRIORITY + 1); t.start(); } - /** - * Only for Restarter - * @since 0.8.12 - */ - public void setIsAlive() { - _isAlive = true; - } - /** * Usage: Router [rebuild] * No other options allowed, for now diff --git a/router/java/src/net/i2p/router/RouterThrottleImpl.java b/router/java/src/net/i2p/router/RouterThrottleImpl.java index 275e4d85a4..d064eb95e3 100644 --- a/router/java/src/net/i2p/router/RouterThrottleImpl.java +++ b/router/java/src/net/i2p/router/RouterThrottleImpl.java @@ -107,7 +107,7 @@ class RouterThrottleImpl implements RouterThrottle { * @return 0 for accept or nonzero reject code */ public int acceptTunnelRequest() { - if (_context.getProperty(Router.PROP_SHUTDOWN_IN_PROGRESS) != null) { + if (_context.router().gracefulShutdownInProgress()) { if (_log.shouldLog(Log.WARN)) _log.warn("Refusing tunnel request since we are shutting down ASAP"); setShutdownStatus(); diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index c10128fe62..fecba78d69 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 6; + public final static long BUILD = 7; /** for example "-test" */ public final static String EXTRA = ""; diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillMonitorJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillMonitorJob.java index f3e84597d2..3771efbb89 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillMonitorJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillMonitorJob.java @@ -66,7 +66,7 @@ class FloodfillMonitorJob extends JobImpl { private boolean shouldBeFloodfill() { // Only if not shutting down... - if (getContext().getProperty(Router.PROP_SHUTDOWN_IN_PROGRESS) != null) + if (getContext().router().gracefulShutdownInProgress()) return false; // Hidden trumps netDb.floodfillParticipant=true diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java index 6c60e3a41c..17b516cac5 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java @@ -429,14 +429,21 @@ class PersistentDataStore extends TransientDataStore { } if (!_initialized) { - if (_facade.reseedChecker().checkReseed(routerCount)) + if (_facade.reseedChecker().checkReseed(routerCount)) { _lastReseed = _context.clock().now(); + // checkReseed will call wakeup() when done and we will run again + } else { + _context.router().setNetDbReady(); + } _initialized = true; } else if (_lastReseed < _context.clock().now() - MIN_RESEED_INTERVAL) { int count = Math.min(routerCount, size()); if (count < MIN_ROUTERS) { if (_facade.reseedChecker().checkReseed(count)) _lastReseed = _context.clock().now(); + // checkReseed will call wakeup() when done and we will run again + } else { + _context.router().setNetDbReady(); } } } diff --git a/router/java/src/net/i2p/router/startup/ClientAppConfig.java b/router/java/src/net/i2p/router/startup/ClientAppConfig.java index 9c3ff4cd6f..3178cbbb8e 100644 --- a/router/java/src/net/i2p/router/startup/ClientAppConfig.java +++ b/router/java/src/net/i2p/router/startup/ClientAppConfig.java @@ -65,7 +65,9 @@ import net.i2p.util.SecureFileOutputStream; */ public class ClientAppConfig { /** wait 2 minutes before starting up client apps */ - private final static long STARTUP_DELAY = 2*60*1000; + private final static long DEFAULT_STARTUP_DELAY = 2*60*1000; + /** speed up i2ptunnel without rewriting clients.config */ + private final static long I2PTUNNEL_STARTUP_DELAY = 35*1000; private static final String PROP_CLIENT_CONFIG_FILENAME = "router.clientConfigFile"; private static final String DEFAULT_CLIENT_CONFIG_FILENAME = "clients.config"; @@ -183,7 +185,10 @@ public class ClientAppConfig { if (onBoot != null) onStartup = "true".equals(onBoot) || "yes".equals(onBoot); - long delay = (onStartup ? 0 : STARTUP_DELAY); + // speed up the start of i2ptunnel for everybody without rewriting clients.config + long delay = onStartup ? 0 : + (className.equals("net.i2p.i2ptunnel.TunnelControllerGroup") ? + I2PTUNNEL_STARTUP_DELAY : DEFAULT_STARTUP_DELAY); if (delayStr != null && !onStartup) try { delay = 1000*Integer.parseInt(delayStr); } catch (NumberFormatException nfe) {} diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java b/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java index cf3cb10534..6849f3d3e6 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java +++ b/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java @@ -62,6 +62,10 @@ class BuildHandler implements Runnable { private final BuildReplyHandler _buildReplyHandler; private final AtomicInteger _currentLookups = new AtomicInteger(); private volatile boolean _isRunning; + private final Object _startupLock = new Object(); + private ExplState _explState = ExplState.NONE; + + private enum ExplState { NONE, IB, OB, BOTH } /** TODO these may be too high, review and adjust */ private static final int MIN_QUEUE = 18; @@ -152,6 +156,30 @@ class BuildHandler implements Runnable { ctx.inNetMessagePool().registerHandlerJobBuilder(VariableTunnelBuildMessage.MESSAGE_TYPE, tbmhjb); ctx.inNetMessagePool().registerHandlerJobBuilder(VariableTunnelBuildReplyMessage.MESSAGE_TYPE, tbrmhjb); } + + /** + * Call the same time you start the threads + * + * @since 0.9.18 + */ + void init() { + // fixup startup state if 0-hop exploratory is allowed in either direction + int ibl = _manager.getInboundSettings().getLength(); + int ibv = _manager.getInboundSettings().getLengthVariance(); + int obl = _manager.getOutboundSettings().getLength(); + int obv = _manager.getOutboundSettings().getLengthVariance(); + boolean ibz = ibl <= 0 || ibl + ibv <= 0; + boolean obz = obl <= 0 || obl + obv <= 0; + if (ibz && obz) { + _explState = ExplState.BOTH; + _context.router().setExplTunnelsReady(); + } else if (ibz) { + _explState = ExplState.IB; + } else if (obz) { + _explState = ExplState.OB; + } + } + /** * @since 0.9 @@ -337,6 +365,38 @@ class BuildHandler implements Runnable { // call buildComplete() after addTunnel() so we don't try another build. _exec.buildComplete(cfg, cfg.getTunnelPool()); _exec.buildSuccessful(cfg); + + if (cfg.getTunnelPool().getSettings().isExploratory()) { + // Notify router that exploratory tunnels are ready + boolean isIn = cfg.isInbound(); + synchronized(_startupLock) { + switch (_explState) { + case NONE: + if (isIn) + _explState = ExplState.IB; + else + _explState = ExplState.OB; + break; + + case IB: + if (!isIn) { + _explState = ExplState.BOTH; + _context.router().setExplTunnelsReady(); + } + break; + + case OB: + if (isIn) { + _explState = ExplState.BOTH; + _context.router().setExplTunnelsReady(); + } + break; + + case BOTH: + break; + } + } + } ExpireJob expireJob = new ExpireJob(_context, cfg, cfg.getTunnelPool()); cfg.setExpireJob(expireJob); diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java b/router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java index 4e74aedb54..f2642b0c00 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java +++ b/router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java @@ -138,6 +138,7 @@ abstract class BuildRequestor { pairedTunnel = mgr.selectOutboundTunnel(); if (pairedTunnel != null && pairedTunnel.getLength() <= 1 && + mgr.getOutboundSettings().getLength() > 0 && mgr.getOutboundSettings().getLength() + mgr.getOutboundSettings().getLengthVariance() > 0) { // don't build using a zero-hop expl., // as it is both very bad for anonomyity, @@ -150,6 +151,7 @@ abstract class BuildRequestor { pairedTunnel = mgr.selectInboundTunnel(); if (pairedTunnel != null && pairedTunnel.getLength() <= 1 && + mgr.getInboundSettings().getLength() > 0 && mgr.getInboundSettings().getLength() + mgr.getInboundSettings().getLengthVariance() > 0) { // ditto pairedTunnel = null; diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java index 1258c10d67..3024063e1a 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java @@ -89,7 +89,7 @@ public class TunnelPool { _lastRateUpdate = _started; _lastLifetimeProcessed = 0; _manager.getExecutor().repoll(); - if (_settings.isInbound() && (_settings.getDestination() != null) ) { + if (_settings.isInbound() && !_settings.isExploratory()) { // we just reconnected and didn't require any new tunnel builders. // however, we /do/ want a leaseSet, so build one LeaseSet ls = null; @@ -117,10 +117,8 @@ public class TunnelPool { } } - TunnelPoolManager getManager() { return _manager; } - void refreshSettings() { - if (_settings.getDestination() != null) { + if (!_settings.isExploratory()) { return; // don't override client specified settings } else { if (_settings.isExploratory()) { @@ -160,7 +158,8 @@ public class TunnelPool { TunnelInfo selectTunnel() { return selectTunnel(true); } private TunnelInfo selectTunnel(boolean allowRecurseOnFail) { - boolean avoidZeroHop = ((getSettings().getLength() + getSettings().getLengthVariance()) > 0); + boolean avoidZeroHop = getSettings().getLength() > 0 && + getSettings().getLength() + getSettings().getLengthVariance() > 0; long period = curPeriod(); synchronized (_tunnels) { @@ -247,7 +246,8 @@ public class TunnelPool { * @since 0.8.10 */ TunnelInfo selectTunnel(Hash closestTo) { - boolean avoidZeroHop = ((getSettings().getLength() + getSettings().getLengthVariance()) > 0); + boolean avoidZeroHop = getSettings().getLength() > 0 && + getSettings().getLength() + getSettings().getLengthVariance() > 0; TunnelInfo rv = null; synchronized (_tunnels) { if (!_tunnels.isEmpty()) { @@ -419,8 +419,7 @@ public class TunnelPool { public boolean isAlive() { return _alive && (_settings.isExploratory() || - (_settings.getDestination() != null && - _context.clientManager().isLocal(_settings.getDestination()))); + _context.clientManager().isLocal(_settings.getDestination())); } /** duplicate of getTunnelCount(), let's pick one */ @@ -439,7 +438,7 @@ public class TunnelPool { LeaseSet ls = null; synchronized (_tunnels) { _tunnels.add(info); - if (_settings.isInbound() && (_settings.getDestination() != null) ) + if (_settings.isInbound() && !_settings.isExploratory()) ls = locked_buildNewLeaseSet(); } @@ -459,7 +458,7 @@ public class TunnelPool { boolean removed = _tunnels.remove(info); if (!removed) return; - if (_settings.isInbound() && (_settings.getDestination() != null) ) + if (_settings.isInbound() && !_settings.isExploratory()) ls = locked_buildNewLeaseSet(); remaining = _tunnels.size(); if (_lastSelected == info) { @@ -478,7 +477,7 @@ public class TunnelPool { for (int i = 0; i < info.getLength(); i++) _context.profileManager().tunnelLifetimePushed(info.getPeer(i), lifetime, lifetimeConfirmed); - if (_alive && _settings.isInbound() && (_settings.getDestination() != null) ) { + if (_alive && _settings.isInbound() && !_settings.isExploratory()) { if (ls != null) { _context.clientManager().requestLeaseSet(_settings.getDestination(), ls); } else { @@ -528,7 +527,7 @@ public class TunnelPool { boolean removed = _tunnels.remove(cfg); if (!removed) return; - if (_settings.isInbound() && (_settings.getDestination() != null) ) + if (_settings.isInbound() && !_settings.isExploratory()) ls = locked_buildNewLeaseSet(); if (_lastSelected == cfg) { _lastSelected = null; @@ -541,7 +540,7 @@ public class TunnelPool { _lifetimeProcessed += cfg.getProcessedMessagesCount(); updateRate(); - if (_settings.isInbound() && (_settings.getDestination() != null) ) { + if (_settings.isInbound() && !_settings.isExploratory()) { if (ls != null) { _context.clientManager().requestLeaseSet(_settings.getDestination(), ls); } @@ -590,7 +589,7 @@ public class TunnelPool { /** noop for outbound and exploratory */ void refreshLeaseSet() { - if (_settings.isInbound() && (_settings.getDestination() != null) ) { + if (_settings.isInbound() && !_settings.isExploratory()) { if (_log.shouldLog(Log.DEBUG)) _log.debug(toString() + ": refreshing leaseSet on tunnel expiration (but prior to grace timeout)"); LeaseSet ls = null; @@ -618,7 +617,7 @@ public class TunnelPool { if (_settings.getAllowZeroHop()) { if ( (_settings.getLength() + _settings.getLengthVariance() > 0) && - (_settings.getDestination() != null) && + (!_settings.isExploratory()) && (_context.profileOrganizer().countActivePeers() > 0) ) { // if it is a client tunnel pool and our variance doesn't allow 0 hop, prefer failure to // 0 hop operation (unless our router is offline) diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java index c559e140ce..d311d46a96 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java @@ -344,9 +344,16 @@ public class TunnelPoolManager implements TunnelManagerFacade { return pool.listTunnels().contains(tunnel); } + /** exploratory */ public TunnelPoolSettings getInboundSettings() { return _inboundExploratory.getSettings(); } + + /** exploratory */ public TunnelPoolSettings getOutboundSettings() { return _outboundExploratory.getSettings(); } + + /** exploratory */ public void setInboundSettings(TunnelPoolSettings settings) { _inboundExploratory.setSettings(settings); } + + /** exploratory */ public void setOutboundSettings(TunnelPoolSettings settings) { _outboundExploratory.setSettings(settings); } public TunnelPoolSettings getInboundSettings(Hash client) { @@ -497,6 +504,7 @@ public class TunnelPoolManager implements TunnelManagerFacade { if (!_executor.isRunning()) { I2PThread t = new I2PThread(_executor, "BuildExecutor", true); t.start(); + _handler.init(); for (int i = 1; i <= _numHandlerThreads; i++) { I2PThread hThread = new I2PThread(_handler, "BuildHandler " + i + '/' + _numHandlerThreads, true); hThread.start();