- All Implemented Interfaces:
ClientApp
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionprivate static classThread factory that sets daemon flag and names threads.(package private) static classThread pool executor for I2P tunnel client handlers.(package private) static classPer-tunnel runner pool state: the explicit per-tunnel ceiling and the lazily created executor.(package private) static classPer-tunnel server-handler pool state: the explicit per-tunnel cap (or -1 for the Tuner-managed default) and the lazily created executor.private classWarning - destroys the singleton.private classStart all configured tunnels.Field Summary
FieldsModifier and TypeFieldDescriptionprivate final StringDirectory of the per-tunnel configs.private final StringFile holding the tunnel definitions.private final I2PAppContextThe app context.private final List<TunnelController> The client and server controllers.private booleanTrue once the configs were read.private final ReadWriteLockLock for the controller list.private ExecutorServiceExecutor service for delayed shutdown tasks, used for cancellation.private ThreadPoolExecutorPool of socket handlers for all clients; volatile so the per-tunnel and per-request read path (getClientExecutor()) never takes_executorLockin steady state.private final ObjectLock for the client thread pool.private static final AtomicLongClient handler threads created.Last elevation warning per tunnel, forshouldLogElevation(Object, long).private static longLast time a hot-path rebalance was run.private final LogLogger for the tunnel controllers.private final ClientAppManagerThe manager that starts the apps.private final ObjectLock for the runner thread pool.private final ConcurrentHashMap<Object, TunnelControllerGroup.RunnerHandler> Private runner pools for client and server tunnels, keyed by tunnel identity (I2PTunnel or I2PTunnelServer).private booleanTrue once the runner pool stats are registered.private static final AtomicLongRunner threads created.private final ObjectLock for the server thread pool.private static final AtomicLongServer handler threads created.private final ConcurrentHashMap<I2PTunnelServer, TunnelControllerGroup.ServerHandler> Private handler pools for server tunnels, keyed by the server instance.private booleanTrue once the server pool stats are registered.private final Map<I2PSession, Set<TunnelController>> Session ownership map for preventing premature session close.private ClientAppStateCurrent state of the tunnel apps.private static booleanFlag to signal that delayed shutdown should be cancelled.private static final intHigh clamp for the Tuner-managed default client cap.private static final intLow clamp for the Tuner-managed default client cap.private static final intCeiling for the concurrent client worker pool.private static intThe Tuner-managed default concurrent-connection cap for client tunnels that do not configure an explicit "i2ptunnel.maxConnections" override.private static intTuned by Tuner.private static final StringConfig directory for split config files.(package private) static final StringDefault config file name.private static booleanFlag to indicate if delayed server tunnel shutdown is in progress.private static longTimestamp when delayed shutdown started, used to calculate remaining wait time.private static final longHow long to wait before dropping an idle thread.private static final char[]From i2psnark Storage.java.private static TunnelControllerGroupThe single instance of the group.private static final intBound on tracked tunnels for_lastElevationLog, cleared wholesale when reached.private static final StringConfig key prefix.(package private) static final long[]Rate stat intervals.private static final longMinimum interval between rebalances triggered from the accept hot path.private static final StringService name for ClientApp registration.(package private) static final intMicro-burst absorption: tasks wait here when every worker is busy before AbortPolicy.private static final longMinimum interval between elevation warnings for one tunnel.(package private) static final intAbsolute floor for a live tunnel's runner pool (shares of clientRunnerMax).(package private) static final intAbsolute floor for a live server tunnel's handler pool.(package private) static final intExtra threads a saturated pool is granted beyond its current load, so a pool that is exactly full can still accept the connection that is being admitted right now instead of rejecting it and then growing.(package private) static final intWorker threads kept for a server tunnel that is currently serving nothing.(package private) static final intHeap-safety ceiling on summed server handler threads (Tuner param, heap-clamped in the Tuner).(package private) static final intPer-tunnel server-handler cap ceiling (Tuner per-tunnel param max).private static final longServer handler idle keepalive.private static intTuned by Tuner: how many inbound connections may wait in the server handler queue.private static intTuned by Tuner: heap-safety ceiling on summed server handler threads across all server tunnels.private static intTuned by Tuner: default per-tunnel ceiling on server handler threads.private static intSocket connect timeout in ms, tuned by Tuner.Constructor Summary
ConstructorsConstructorDescriptionTunnelControllerGroup(I2PAppContext context, ClientAppManager mgr, String[] args) Instantiation only.Method Summary
Modifier and TypeMethodDescription(package private) voidacquire(TunnelController controller, I2PSession session) Note the fact that the controller is using the session so that it isn't destroyed prematurely.voidaddController(TunnelController controller) Add the given tunnel to the set of known controllers (but don't add it to a config file or start it or anything).(package private) static int[]allocateServerThreads(int budget, int[] desired, int floor) Allocate a global server-handler thread budget across the live server tunnels, preserving each tunnel's ceiling (desired) and a shared floor.private FileReturn the config File associated with a TunnelController or a default File based on the tunnel name.voidCancel a delayed server tunnel shutdown and restart any servers that were stopped.private voidchangeState(ClientAppState state) Change the client app state.private voidchangeState(ClientAppState state, Exception e) Change the client app state and notify the manager.(package private) static intclaimRunnerShare(int cap, int queueDepth, int active) Load-aware runner-pool claim: same semantics asclaimServerHandlerShare(int, int, int, int)with the runner floor.(package private) static intclaimServerHandlerShare(int cap, int floor, int queueDepth, int active) Load-aware claim for one server tunnel's share of the global handler budget.private voidcleanupShutdown(List<TunnelController> stoppedServers) Stop the shutdown executor, restarting any servers that were stopped if the shutdown was cancelled.Fetch and clear all outstanding messages from any of the known tunnels.(package private) static ThreadPoolExecutorcreateRunnerExecutor(int maxThreads, AtomicLong index) Create a thread-per-connection runner pool for one tunnel.(package private) static ThreadPoolExecutorcreateServerExecutor(int threads, AtomicLong index) Create a bounded executor that runs inbound server-tunnel connection handlers.(package private) static ThreadPoolExecutorcreateServerExecutor(int threads, AtomicLong index, long keepAliveMs) AscreateServerExecutor(int, AtomicLong)but with an explicit worker keepalive.private voidStop all tunnels.(package private) static intelevatedRunnerMax(int currentThreads, int ceiling) How many threads a pool pinned atcurrentwith work queued may be elevated to above its ceiling, and how often the elevation is logged.private voidRegister the per-tunnel runner pool rate stats exactly once.private voidRegister the server-handler rate stats exactly once, on first pool use.static intThe Tuner-managed default concurrent-connection cap for unset client tunnels.(package private) ThreadPoolExecutorThe executor pool for client tunnel tasks.(package private) ThreadPoolExecutorgetClientRunnerExecutor(I2PTunnel tunnel, int ceiling) Get (creating and sizing on first use) the private runner pool for a client tunnel.static intThe maximum number of concurrent client connections.Retrieves the application context the group was created with.Retrieve a list of tunnels known.private List<TunnelController> getControllers(File cfgFile) Retrieve a list of tunnels known.private List<TunnelController> Collect the running server tunnels that have a shutdown delay.The display name of this tunnel group, for the ClientApp interface.static TunnelControllerGroupIn I2PAppContext will instantiate if necessary and always return non-null.static TunnelControllerGroupgetInstance(I2PAppContext ctx) In I2PAppContext will instantiate if necessary and always return non-null.static doubleThe busiest per-tunnel I/O transfer pool's utilization: its active plus queued transfers divided by its present maximum, as a rate that may exceed 1.0 when the queue backs up past the pool's ceiling.static longI/O stall timeout in ms for Server→Client transfers.static intActive thread count in the I/O transfer pool (live saturation signal).static intI/O transfer pool size for Server→Client data forwarding.static intLive aggregate active thread count across all server tunnel executor pools.static intLive aggregate max pool size across all server tunnel executor pools.static intLive aggregate queue depth across all server tunnel executor pools.intGet the maximum configured shutdown delay among all server tunnels.getName()The registered name of this tunnel group, for the ClientApp interface.static intGet the remaining time until the longest-delayed server tunnel will stop.private ThreadPoolExecutorgetRunnerExecutor(Object key, int ceiling) Shared create-or-get for a per-tunnel runner pool.static intThe maximum number of connections that may wait in the server handler bounded queue before overflow rejection (ThreadPoolExecutor.AbortPolicy).(package private) ThreadPoolExecutorgetServerExecutor(I2PTunnelServer server, int threadOverride) Get (creating and sizing on first use) the private handler pool for a server tunnel.static intThe number of server handler threads available in total across all server tunnels: the heap-safety ceiling for the per-tunnel handler pools.(package private) ThreadPoolExecutorgetServerRunnerExecutor(I2PTunnelServer server, int ceiling) Get (creating and sizing on first use) the private runner pool for a server tunnel's connection handlers (HTTP runner, CompressedRequestor, IRC/DCC).static intThe default per-tunnel ceiling on server handler threads (the private handler-pool size for a server tunnel that does not set an expliciti2ptunnel.server.threadsoverride).static intThe socket connect timeout in milliseconds.static longTotal stall events detected since router start (monotonically increasing).getState()ClientApp interface.(package private) intgrowRunnerExecutorForLoad(Object key, int queueDepth, int active) Grow one server tunnel's runner pool to match the load it is actually seeing, elevating past the ceiling and warning when demand warrants.(package private) voidGrow one server's handler pool to match the load it is actually seeing.static booleanCheck if delayed server tunnel shutdown is currently in progress.private voidShutdown the client executor and all per-tunnel runner pools, waiting for termination of the shared fallback pool.private voidShutdown the per-tunnel server handler pools.List all the config files in the config directory, or the monolithic config file, if they exist.private List<Properties> loadConfig(File cfgFile) Load up the config data from either type of config file automatically.voidloadControllers(File cfgFile) Load up all of the tunnels configured in the given file.private voidloadControllers(File cfgFile, boolean shouldMigrate) Load the controllers from the config file.static voidLoad the tunnel configs from a file, for running standalone.private booleanmigrate(List<Properties> tunnels, File from, File dir) Copies old tunnel configs into the new format.(package private) static intnormalizeThreadOverride(int v) The per-tunnel thread cap as used bygetServerExecutor(I2PTunnelServer, int)andrebalanceServerExecutors(); a value outside the valid cap range means "follow the Tuner-managed default".(package private) voidnoteRunnerIdle(Object key) Note that a submit found this pool quiet, and drop a stale elevation if the tunnel has done enough work while quiet to say the burst is over.voidPrepare for graceful shutdown by stopping delayed server tunnels.private voidTrigger a full handler-pool rebalance after a global or per-tunnel cap change.private voidTrigger a full runner-pool rebalance after a global budget change.private voidRebalance every live runner pool from the global clientRunnerMax budget and each tunnel's ceiling.(package private) static voidTrigger a load-aware runner-pool rebalance from the accept hot path after a RejectedExecutionException: an idle sibling may shrink its claim and free budget for this tunnel before the retry.private voidRebalance every live server-tunnel pool from the global budget, the per-tunnel caps, and the per-tunnel floor.(package private) voidrefreshServerThreadOverride(I2PTunnelServer server, int threadOverride) Recompute and apply a server tunnel's per-tunnel thread cap after a live config change (optionsUpdated), resizing the private pool when the override differs from what was registered.(package private) voidrelease(TunnelController controller, I2PSession session) Note the fact that the controller is no longer using the session, and if no other controllers are using it, destroy the session.voidStop all tunnels, reload config, and restart those configured to do so.voidRemove the configuration of this tunnel only.removeController(TunnelController controller) Stop and remove the given tunnel.(package private) voidresizeClientExecutor(int newMax) Resize the shared (fallback) client executor max pool size.private static voidresizeRunnerExecutor(ThreadPoolExecutor ex, int newMax) Resize a per-tunnel runner pool, keeping core == max so the burst queue is only reached after every worker is busy.private static voidresizeServerExecutor(ThreadPoolExecutor ex, int newThreads) Resize a server-tunnel handler pool, preserving a fixed core == max pool.private FileresolveConfig(String name) Resolve a config path to an absolute file under the config dir.Restart all tunnels.(package private) static intThe runner ceiling for one pool, read now rather than remembered.(package private) voidrunnerStopped(Object key) Deregister a stopped tunnel's runner pool: shut it down (queued burst tasks drain, then workers exit) and redistribute its share of the global budget to the remaining tunnels.(package private) static voidSample a runner pool's live active-thread count into the Tuner primary stati2ptunnel.clientRunner.activeThreads.private static StringReplace problematic characters in file name.private voidsaveConfig(File cfgFile) Save the configuration of all known tunnels to the given file.voidSave the configuration of this tunnel only, may be new.(package private) static int[]scaleDemandsToCeiling(int[] demand, int ceiling) Scale per-pool demands down to a global ceiling, proportionally.private voidscheduleDelayedShutdowns(List<TunnelController> delayedServers, List<TunnelController> stoppedServers) Submit one shutdown task per delayed server, each waiting a random delay in the configured range before stopping its tunnel.(package private) voidserverStopped(I2PTunnelServer server) Deregister a stopped server tunnel: shut down its private pool (queued tasks drain, then the worker threads exit) and redistribute its share of the global budget to the remaining tunnels.(package private) static intserverThreadsForDemand(int queueDepth, int active, int cap) Worker threads one server tunnel needs, from its own demand alone.static voidsetClientDefaultMaxConnections(int val) Clamp and set the Tuner-managed default concurrent-connection cap, 256 to 16384.static voidsetClientRunnerMax(int val) Clamp and set the maximum concurrent client connections, 4 to 16384.static voidsetIOStallTimeoutMs(long val) Set the I/O stall timeout in ms (clamped to [5000, 300000]).static voidsetIOTransferThreads(int val) Set the I/O transfer pool size (clamped to [2, 64]).static voidsetServerBacklogQueueCapacity(int val) Clamp and set the server handler queue capacity, 16 to 65536.static voidsetServerHandlerThreads(int val) Clamp and set the heap-safety ceiling on summed server handler threads, 2 to 16384.static voidsetServerThreadsPerTunnel(int val) Clamp and set the default per-tunnel ceiling on server handler threads, 2 to 4096.static voidsetSocketConnectTimeout(int val) Clamp and set the socket connect timeout, 5000 to 120000 ms.private String[]setupArguments(String[] args) Reads argument list and returns two strings that can be used to instantiate _configFile and _configDirectory.(package private) static booleanshouldElevateRunner(int queueDepth, int currentThreads, int ceiling) Whether a pinned pool with this backlog should be elevated past its ceiling.(package private) booleanshouldLogElevation(Object key, long now) Whether to log an elevation for this tunnel now.private booleanDetects whether a migration to split configuration files should/will/has happened based on the platform and installation type.(package private) static booleanshouldReleaseElevation(long completed, long idleSince, int elevatedMax) Whether an elevation has gone stale and should be released.voidshutdown()Convenience method - calls shutdown(true) to wait for.voidshutdown(boolean waitForDelayed) Warning - destroys the singleton! Caller must root a new context before calling instance() ormain(String[])again.voidClientApp interface.private voidStop server tunnels with shutdown delays independently.private List<Properties> splitMonolithicConfig(Properties config) Split up the config data loaded from a single file, this is the old version for the numbered config file, into properties one for each tunnel.Start all tunnels.private voidStart all of the tunnels.voidstartup()ClientApp interface.Stop all tunnels.voidStop and remove reference to all known tunnels (but don't delete any config file or do other silly things).
Field Details
_log
Logger for the tunnel controllers._state
Current state of the tunnel apps._context
The app context._mgr
The manager that starts the apps.instance
The single instance of the group.DEFAULT_CONFIG_FILE
CONFIG_DIR
Config directory for split config files.- See Also:
PREFIX
_controllers
The client and server controllers._controllersLock
Lock for the controller list._controllersLoaded
private boolean _controllersLoadedTrue once the configs were read._configFile
File holding the tunnel definitions._configDirectory
Directory of the per-tunnel configs.REGISTERED_NAME
Service name for ClientApp registration.- See Also:
delayedShutdownInProgress
private static volatile boolean delayedShutdownInProgressFlag to indicate if delayed server tunnel shutdown is in progress. Set byshutdownDelayedServers()and checked by GracefulShutdown.delayedShutdownStartTime
private static volatile long delayedShutdownStartTimeTimestamp when delayed shutdown started, used to calculate remaining wait time.cancelDelayedShutdown
private static volatile boolean cancelDelayedShutdownFlag to signal that delayed shutdown should be cancelled. Set bycancelDelayedShutdown()and checked by shutdown tasks._delayedShutdownExecutor
Executor service for delayed shutdown tasks, used for cancellation._sessions
Session ownership map for preventing premature session close._executor
Pool of socket handlers for all clients; volatile so the per-tunnel and per-request read path (getClientExecutor()) never takes_executorLockin steady state. Mutation (create/resize/kill) stays under the lock._executorThreadCount
Client handler threads created._executorLock
Lock for the client thread pool.HANDLER_KEEPALIVE_MS
private static final long HANDLER_KEEPALIVE_MSHow long to wait before dropping an idle thread.- See Also:
_serverHandlers
private final ConcurrentHashMap<I2PTunnelServer, TunnelControllerGroup.ServerHandler> _serverHandlersPrivate handler pools for server tunnels, keyed by the server instance. Each server tunnel owns its pool so a saturated destination (its threads and its queue full) rejects only ITS excess connections; a flood on one port can no longer consume threads that other ports need._serverExecutorThreadCount
Server handler threads created._serverExecutorLock
Lock for the server thread pool._serverHandlerStatsRegistered
private boolean _serverHandlerStatsRegisteredTrue once the server pool stats are registered.SERVER_KEEPALIVE_MS
private static final long SERVER_KEEPALIVE_MSServer handler idle keepalive.- See Also:
SERVER_HANDLER_MAX_THREADS
static final int SERVER_HANDLER_MAX_THREADSHeap-safety ceiling on summed server handler threads (Tuner param, heap-clamped in the Tuner).- See Also:
SERVER_HANDLER_PER_TUNNEL_MAX
static final int SERVER_HANDLER_PER_TUNNEL_MAXPer-tunnel server-handler cap ceiling (Tuner per-tunnel param max).- See Also:
SERVER_HANDLER_FLOOR
static final int SERVER_HANDLER_FLOORAbsolute floor for a live server tunnel's handler pool.- See Also:
SERVER_HANDLER_IDLE
static final int SERVER_HANDLER_IDLEWorker threads kept for a server tunnel that is currently serving nothing. Deliberately 1, notSERVER_HANDLER_FLOOR: a fixed floor is charged against every open server tunnel before any demand weighting is applied, so on a router with many eepsites it consumed almost the entire budget and left the busiest tunnel with a handful of threads while it rejected connections. An idle tunnel costs one thread and grows on its own demand.- See Also:
SERVER_HANDLER_HEADROOM
static final int SERVER_HANDLER_HEADROOMExtra threads a saturated pool is granted beyond its current load, so a pool that is exactly full can still accept the connection that is being admitted right now instead of rejecting it and then growing.- See Also:
_runnerPools
Private runner pools for client and server tunnels, keyed by tunnel identity (I2PTunnel or I2PTunnelServer). Each pool is a share of the global clientRunnerMax budget so a saturated dest/tunnel cannot consume every runner thread. Rejection is per-tunnel (AbortPolicy after the short burst queue fills) rather than global._runnerPoolThreadCount
Runner threads created._runnerPoolLock
Lock for the runner thread pool._runnerPoolStatsRegistered
private boolean _runnerPoolStatsRegisteredTrue once the runner pool stats are registered.RUNNER_POOL_FLOOR
static final int RUNNER_POOL_FLOORAbsolute floor for a live tunnel's runner pool (shares of clientRunnerMax).- See Also:
RUNNER_BURST_QUEUE
static final int RUNNER_BURST_QUEUEMicro-burst absorption: tasks wait here when every worker is busy before AbortPolicy. Small enough that a stuck flood still rejects promptly (and sheds with a real HTTP page), large enough to cover a browser's parallel open (typically ~6) plus a short spike.- See Also:
serverHandlerThreads
private static volatile int serverHandlerThreadsTuned by Tuner: heap-safety ceiling on summed server handler threads across all server tunnels.clientRunnerMax
private static volatile int clientRunnerMaxTuned by Tuner.serverBacklogQueueCapacity
private static volatile int serverBacklogQueueCapacityTuned by Tuner: how many inbound connections may wait in the server handler queue.serverThreadsPerTunnel
private static volatile int serverThreadsPerTunnelTuned by Tuner: default per-tunnel ceiling on server handler threads.CLIENT_RUNNER_MAX_MAX
private static final int CLIENT_RUNNER_MAX_MAXCeiling for the concurrent client worker pool.- See Also:
CLIENT_MAX_CONNECTIONS_MIN
private static final int CLIENT_MAX_CONNECTIONS_MINLow clamp for the Tuner-managed default client cap.- See Also:
CLIENT_MAX_CONNECTIONS_MAX
private static final int CLIENT_MAX_CONNECTIONS_MAXHigh clamp for the Tuner-managed default client cap.- See Also:
clientDefaultMaxConnections
private static volatile int clientDefaultMaxConnectionsThe Tuner-managed default concurrent-connection cap for client tunnels that do not configure an explicit "i2ptunnel.maxConnections" override. Kept separate fromclientRunnerMax(the shared HTTP proxy executor ceiling) because that cap bounds the worker pool, whereas this cap gates the accept loop from over-dispatching before a handler thread exists.An explicit per-tunnel override always wins over this default; the Tuner can only move the floor for tunnels that leave the cap at default. Raised under sustained load to defer connection shedding (which the browser reports as an empty response), lowered under idle to conserve memory/FDs.
socketConnectTimeout
private static volatile int socketConnectTimeoutSocket connect timeout in ms, tuned by Tuner.RATES
static final long[] RATESRate stat intervals.ILLEGAL
private static final char[] ILLEGALFrom i2psnark Storage.java.RUNNER_ELEVATION_LOG_INTERVAL_MS
private static final long RUNNER_ELEVATION_LOG_INTERVAL_MSMinimum interval between elevation warnings for one tunnel.- See Also:
MAX_TRACKED_ELEVATION_TUNNELS
private static final int MAX_TRACKED_ELEVATION_TUNNELSBound on tracked tunnels for_lastElevationLog, cleared wholesale when reached. The map is keyed by tunnel instance, which is bounded by the number of tunnels this router runs, so the cap only guards against a leak from tunnels created and destroyed repeatedly.- See Also:
_lastElevationLog
Last elevation warning per tunnel, forshouldLogElevation(Object, long).REBALANCE_THROTTLE_MS
private static final long REBALANCE_THROTTLE_MSMinimum interval between rebalances triggered from the accept hot path.- See Also:
_lastRebalanceAt
private static volatile long _lastRebalanceAtLast time a hot-path rebalance was run.
Constructor Details
TunnelControllerGroup
Instantiation only. Caller must callstartup(). Config file problems will not throw exception until startup().- Parameters:
context- the context the tunnels are created inmgr- may be nullargs- zero or one args, which may be one config file or one config directory. If not absolute will be relative to the context's config dir, if empty or null, the default isi2ptunnel.configfor a config file andi2ptunnel.config.dfor a config directory- Throws:
IllegalArgumentException- if too many args
Method Details
getServerHandlerThreads
public static int getServerHandlerThreads()The number of server handler threads available in total across all server tunnels: the heap-safety ceiling for the per-tunnel handler pools.- Returns:
- the global server handler thread budget
setServerHandlerThreads
public static void setServerHandlerThreads(int val) Clamp and set the heap-safety ceiling on summed server handler threads, 2 to 16384. This is not a budget the pools divide: each pool is sized from its own inbound demand byserverThreadsForDemand(int, int, int)and capped by its per-tunnel ceiling (getServerThreadsPerTunnel()), and this value only trims every pool proportionally viascaleDemandsToCeiling(int[], int)when the total is unsupportable. The high ceiling lets the Tuner keep pace with sustained inbound load where workers stall on slow inbound I2P writes rather than finishing quickly; the memory-derived clamp in the Tuner keeps the reachable ceiling at what the max heap can host. Live pools are rebalanced to the new ceiling.- Parameters:
val- the desired ceiling
getServerBacklogQueueCapacity
public static int getServerBacklogQueueCapacity()The maximum number of connections that may wait in the server handler bounded queue before overflow rejection (ThreadPoolExecutor.AbortPolicy).- Returns:
- the queue capacity
setServerBacklogQueueCapacity
public static void setServerBacklogQueueCapacity(int val) Clamp and set the server handler queue capacity, 16 to 65536. The Tuner I2PTunnelServerBacklogParam stays within this same range.- Parameters:
val- the desired capacity
getIOTransferThreads
public static int getIOTransferThreads()I/O transfer pool size for Server→Client data forwarding. Delegates toI2PTunnelHTTPServer.- Returns:
- the current I/O transfer thread count
setIOTransferThreads
public static void setIOTransferThreads(int val) Set the I/O transfer pool size (clamped to [2, 64]).- Parameters:
val- desired pool size
getIOTransferActiveCount
public static int getIOTransferActiveCount()Active thread count in the I/O transfer pool (live saturation signal).- Returns:
- active threads, or 0 if pool not yet created
getIOMaxPoolUtilization
public static double getIOMaxPoolUtilization()The busiest per-tunnel I/O transfer pool's utilization: its active plus queued transfers divided by its present maximum, as a rate that may exceed 1.0 when the queue backs up past the pool's ceiling. Delegates toI2PTunnelHTTPServer.Per-pool, not a global sum — the tuner grows the shared budget from this so one saturated pool stays visible however quiet the others are.
- Returns:
- the busiest pool's utilization rate, or 0.0 when no pools exist
getIOStallTimeoutMs
public static long getIOStallTimeoutMs()I/O stall timeout in ms for Server→Client transfers.- Returns:
- the current stall timeout
setIOStallTimeoutMs
public static void setIOStallTimeoutMs(long val) Set the I/O stall timeout in ms (clamped to [5000, 300000]).- Parameters:
val- desired timeout in ms
getStallEventCount
public static long getStallEventCount()Total stall events detected since router start (monotonically increasing). Used by the Tuner as a real-time signal for stall timeout adjustment.- Returns:
- total stall event count
getLiveServerHandlerQueueDepth
public static int getLiveServerHandlerQueueDepth()Live aggregate queue depth across all server tunnel executor pools. Reads directly from eachThreadPoolExecutor.getQueue()'ssize, bypassing the Rate-averaged stat for instant saturation detection.- Returns:
- total queued tasks across all server handler pools, or 0 if none
getLiveServerHandlerActiveCount
public static int getLiveServerHandlerActiveCount()Live aggregate active thread count across all server tunnel executor pools. Reads directly from eachThreadPoolExecutor.getActiveCount(), bypassing the Rate-averaged stat for instant saturation detection.- Returns:
- total active threads across all server handler pools, or 0 if none
getLiveServerHandlerMaxThreads
public static int getLiveServerHandlerMaxThreads()Live aggregate max pool size across all server tunnel executor pools. The sum of each pool'sThreadPoolExecutor.getMaximumPoolSize(), representing total handler capacity.- Returns:
- total max threads across all server handler pools, or 0 if none
getServerThreadsPerTunnel
public static int getServerThreadsPerTunnel()The default per-tunnel ceiling on server handler threads (the private handler-pool size for a server tunnel that does not set an expliciti2ptunnel.server.threadsoverride). The pool for any server tunnel is at most this many threads, bounded in total by the global budget (getServerHandlerThreads()).- Returns:
- the default per-tunnel cap
setServerThreadsPerTunnel
public static void setServerThreadsPerTunnel(int val) Clamp and set the default per-tunnel ceiling on server handler threads, 2 to 4096. The TunerI2PTunnelServerThreadsParamstays within this range.- Parameters:
val- the desired default per-tunnel cap
getClientRunnerMax
public static int getClientRunnerMax()The maximum number of concurrent client connections.- Returns:
- the client runner max
setClientRunnerMax
public static void setClientRunnerMax(int val) Clamp and set the maximum concurrent client connections, 4 to 16384. The Tuner I2PTunnelClientRunnerMaxParam stays within this same range.Enforces a one-way invariant against the Tuner-managed admission gate (
clientDefaultMaxConnections): the worker pool may never be shrunk below what the gate admits. Connections are served thread-per-connection with only a shortRUNNER_BURST_QUEUEof slack, so every admitted connection needs its own worker; if the runner were allowed to collapse below the gate, bursts would overflow the worker pool and be closed with zero bytes as empty responses. Flooring the runner at the gate keeps admission and serving capacity in the same ballpark; the runner may still grow above the gate to absorb bursts.- Parameters:
val- the desired maximum
getClientDefaultMaxConnections
public static int getClientDefaultMaxConnections()The Tuner-managed default concurrent-connection cap for unset client tunnels.- Returns:
- the default caps, 256 to 16384
setClientDefaultMaxConnections
public static void setClientDefaultMaxConnections(int val) Clamp and set the Tuner-managed default concurrent-connection cap, 256 to 16384. The TunerI2PTunnelClientMaxConnectionsParamstays within this range.- Parameters:
val- the desired default cap
getSocketConnectTimeout
public static int getSocketConnectTimeout()The socket connect timeout in milliseconds.- Returns:
- the socket connect timeout
setSocketConnectTimeout
public static void setSocketConnectTimeout(int val) Clamp and set the socket connect timeout, 5000 to 120000 ms.- Parameters:
val- the timeout in milliseconds
getInstance
In I2PAppContext will instantiate if necessary and always return non-null. As of 0.9.4, when in RouterContext, will return null if the TCG has not yet been started by the router. As of 0.9.41, that's true for Android as well.In Android, this should be used for all calls except from LoadClientsJob, as we do not want to instantiate TCG too early. Android must do null checks on the return value.
- Returns:
- the shared group, or null in RouterContext before the router starts it
- Throws:
IllegalArgumentException- if unable to load fromi2ptunnel.config
getInstance
In I2PAppContext will instantiate if necessary and always return non-null. When in RouterContext, will return null (except in Android) if the TCG has not yet been started by the router. In Android, if the old instance uses a stale context, it will replace it.In Android, this should only be called from LoadClientsJob, as we do not want to instantiate TCG too early.
- Parameters:
ctx- the context the instance is built against, replacing a stale Android one- Returns:
- the shared group, or null in RouterContext before the router starts it
- Throws:
IllegalArgumentException- if unable to load fromi2ptunnel.config
setupArguments
Reads argument list and returns two strings that can be used to instantiate _configFile and _configDirectory. After calling this method, the returned Strings must be assigned to _configFile and _configDirectory.- Parameters:
args- must be the args passed to TunnelControllerGroup.- Returns:
- an array of exactly 2 strings, where [0] is the the value for _configFile and [1] is the value for _configDirectory
resolveConfig
main
Load the tunnel configs from a file, for running standalone.- Parameters:
args- one arg, the config file, if not absolute will be relative to the context's config dir, if no args, the default isi2ptunnel.config- Throws:
IllegalArgumentException- if unable to load from config from file
getContext
Retrieves the application context the group was created with.- Returns:
- the context this group was built against
startup
public void startup()ClientApp interface.- Specified by:
startupin interfaceClientApp- Throws:
IllegalArgumentException- if unable to load config from file
getState
ClientApp interface.getName
getDisplayName
The display name of this tunnel group, for the ClientApp interface.- Specified by:
getDisplayNamein interfaceClientApp- Returns:
- the display name
changeState
Change the client app state.- Parameters:
state- the new state
changeState
Change the client app state and notify the manager.- Parameters:
state- the new statee- cause of the change, null if there is none
shutdown
shutdown
public void shutdown(boolean waitForDelayed) Warning - destroys the singleton! Caller must root a new context before calling instance() ormain(String[])again. Aggressively kill and null everything to reduce memory usage in the JVM after stopping, and to recognize what must be reinitialized on restart (Android).- Parameters:
waitForDelayed- true to wait for shutdown-delayed tunnels, false for immediate
shutdown
public void shutdown()Convenience method - calls shutdown(true) to wait for.prepareGracefulShutdown
public void prepareGracefulShutdown()Prepare for graceful shutdown by stopping delayed server tunnels. This should be called during graceful shutdown period, before final shutdown is triggered. It stops server tunnels with delays but does not unload controllers or release resources.isDelayedShutdownInProgress
public static boolean isDelayedShutdownInProgress()Check if delayed server tunnel shutdown is currently in progress.- Returns:
- true if shutdownDelayedServers() is currently executing
getMaxShutdownDelay
public int getMaxShutdownDelay()Get the maximum configured shutdown delay among all server tunnels.- Returns:
- maximum delay in seconds, or 0 if no servers have delays configured
getRemainingShutdownDelay
public static int getRemainingShutdownDelay()Get the remaining time until the longest-delayed server tunnel will stop.- Returns:
- remaining delay in seconds, or 0 if shutdown not in progress or delay has passed
cancelDelayedShutdown
public void cancelDelayedShutdown()Cancel a delayed server tunnel shutdown and restart any servers that were stopped. This should be called when the user cancels a graceful router shutdown/restart.shutdownDelayedServers
private void shutdownDelayedServers()Stop server tunnels with shutdown delays independently. Each server with a delay gets its own timer and stops independently.synchronized so concurrent invocations (e.g. a cancel racing a repeated graceful-shutdown) cannot both create and tear down the shared
_delayedShutdownExecutor; otherwise one thread's cleanup can null the field while another is still dereferencing it.getDelayedServers
Collect the running server tunnels that have a shutdown delay.- Returns:
- the delayed servers, empty if none
scheduleDelayedShutdowns
private void scheduleDelayedShutdowns(List<TunnelController> delayedServers, List<TunnelController> stoppedServers) Submit one shutdown task per delayed server, each waiting a random delay in the configured range before stopping its tunnel.- Parameters:
delayedServers- the servers to stopstoppedServers- the list to record the stopped servers in
cleanupShutdown
Stop the shutdown executor, restarting any servers that were stopped if the shutdown was cancelled.- Parameters:
stoppedServers- the servers stopped by the shutdown tasks
shouldMigrate
private boolean shouldMigrate()Detects whether a migration to split configuration files should/will/has happened based on the platform and installation type. Does not tell whether a migration has actually occurred.- Returns:
- true if a migration is relevant to the platform, false if not
loadControllers
Load up all of the tunnels configured in the given file. Prior to 0.9.20, also started the tunnels. As of 0.9.20, does not start the tunnels, you must call startup() orgetInstance()instead of loadControllers().DEPRECATED for use outside this class. Use startup() or getInstance().
- Parameters:
cfgFile- the config file or directory to read the tunnel definitions from- Throws:
IllegalArgumentException- if unable to load from file
loadControllers
Load the controllers from the config file.- Parameters:
cfgFile- the i2ptunnel config file to readshouldMigrate- migrate to, and load from,i2ptunnel.config.d- Throws:
IllegalArgumentException- if unable to load from file
migrate
Copies old tunnel configs into the new format.- Parameters:
tunnels- the tunnel configs to migratefrom- the directory of the old configsdir- the directory to write the new configs to- Returns:
- true if any config was migrated
startControllers
private void startControllers()Start all of the tunnels. Must callloadControllers(File)first.reloadControllers
public void reloadControllers()Stop all tunnels, reload config, and restart those configured to do so. WARNING - Does NOT simply reload the configuration!!! This is probably not what you want. This does not return or clear the controller messages.- Throws:
IllegalArgumentException- if unable to reload config file
unloadControllers
public void unloadControllers()Stop and remove reference to all known tunnels (but don't delete any config file or do other silly things).addController
Add the given tunnel to the set of known controllers (but don't add it to a config file or start it or anything).- Parameters:
controller- the tunnel to add to the known set
removeController
Stop and remove the given tunnel. Side effect - clears all messages the controller. Does NOT delete the configuration - must callsaveConfig()or removeConfig() also.- Parameters:
controller- the tunnel to stop and drop, where null yields an empty message list- Returns:
- list of messages from the controller as it is stopped
stopAllControllers
destroyAllControllers
private void destroyAllControllers()Stop all tunnels. They may not be restarted, you must reload. Caller must synch. Caller must clear controller list.startAllControllers
restartAllControllers
clearAllMessages
saveConfig
Save the configuration of all known tunnels to the given file. Side effect: for split config, sets "confFile" property to absolute path.- Parameters:
cfgFile- the file to write all tunnel configs to- Throws:
IOException- if the file cannot be created or written
saveConfig
Save the configuration of this tunnel only, may be new. Side effect: for split config, sets "confFile" property to absolute path.- Parameters:
tc- the controller whose configuration is saved- Throws:
IOException- if the config file cannot be written
removeConfig
Remove the configuration of this tunnel only.- Parameters:
tc- the controller whose config file is renamed to a .bak and deleted- Throws:
IOException- if the config file cannot be renamed
sanitize
assureConfigFile
Return the config File associated with a TunnelController or a default File based on the tunnel name.- Parameters:
tc- the controller whose config file is wanted- Returns:
- the File ready for use
listFiles
loadConfig
Load up the config data from either type of config file automatically. Side effect: for split config, sets "confFile" property to absolute path.- Parameters:
cfgFile- the monolithic or split config file to read- Returns:
- non-null, properties loaded, one for each tunnel
- Throws:
IOException- if unable to load from file
splitMonolithicConfig
Split up the config data loaded from a single file, this is the old version for the numbered config file, into properties one for each tunnel.- Parameters:
config- the monolithic properties with numbered tunnel key prefixes- Returns:
- non-null, properties loaded, one for each tunnel
getControllers
Retrieve a list of tunnels known.Side effect: if the tunnels have not been loaded from config yet, they will be.
- Returns:
- list of TunnelController objects
- Throws:
IllegalArgumentException- if unable to load config from file
getControllers
Retrieve a list of tunnels known.Side effect: if the tunnels have not been loaded from config yet, they will be.
- Parameters:
cfgFile- the config file to load from if not loaded yet- Returns:
- list of TunnelController objects
- Throws:
IllegalArgumentException- if unable to load config from file
acquire
Note the fact that the controller is using the session so that it isn't destroyed prematurely.- Parameters:
controller- the controller claiming a reference to the sessionsession- the session being claimed
release
Note the fact that the controller is no longer using the session, and if no other controllers are using it, destroy the session.- Parameters:
controller- the controller giving up its reference to the sessionsession- the session being released
getClientExecutor
ThreadPoolExecutor getClientExecutor()The executor pool for client tunnel tasks.Read path is lock-free in steady state (
_executoris volatile): the common case is a null-and-no-change return with no synchronized acquisition, so per-request callers (I2PTunnelHTTPClientRunner) are not serialized on the global_executorLockand the Tuner load sample is not written inside a monitor. Create/resize races resolve under the lock with a double-check.- Returns:
- non-null
getServerExecutor
Get (creating and sizing on first use) the private handler pool for a server tunnel. All pools are sized together so their sum never exceeds the Tuner-managed global budget while each pool respects its per-tunnel ceiling. Registration records the tunnel's expliciti2ptunnel.server.threadscap (or -1 to follow the Tuner-managed default), rebalancing every live pool to make room for the newcomer.Tasks are short-lived (µs-scale), so core threads handle bursts via a bounded queue. Overflow throws
RejectedExecutionException(AbortPolicy) rather than running the handler inline on the accept thread, so the accept loop can never be pinned by a write-blocked handler, and the rejection is per-port: only the saturated tunnel's own connections are dropped.- Parameters:
server- the server tunnel requesting a pool; never nullthreadOverride- the per-tunnel thread cap (>=2), or -1 for default- Returns:
- non-null, the server's private executor
refreshServerThreadOverride
Recompute and apply a server tunnel's per-tunnel thread cap after a live config change (optionsUpdated), resizing the private pool when the override differs from what was registered.- Parameters:
server- the running server tunnel; ignored if not registeredthreadOverride- the new per-tunnel thread cap (>=2), or -1 for default
serverStopped
Deregister a stopped server tunnel: shut down its private pool (queued tasks drain, then the worker threads exit) and redistribute its share of the global budget to the remaining tunnels.- Parameters:
server- the stopped server tunnel
getServerRunnerExecutor
Get (creating and sizing on first use) the private runner pool for a server tunnel's connection handlers (HTTP runner, CompressedRequestor, IRC/DCC). Split from the client runner budget so inbound server load cannot starve client tunnels and vice versa. Each pool is a share of the global clientRunnerMax budget.- Parameters:
server- the server tunnel requesting a pool; never nullceiling- per-tunnel max-connections ceiling (>0), or <=0 for the default- Returns:
- non-null, the server's private runner executor
getClientRunnerExecutor
Get (creating and sizing on first use) the private runner pool for a client tunnel. Ceiling is the tunnel's effective maxConnections so a flood on one dest cannot consume runners other tunnels need.- Parameters:
tunnel- the client tunnel requesting a pool; may be null (falls back to the global executor)ceiling- per-tunnel max-connections ceiling (>0), or <=0 for the default- Returns:
- non-null, the tunnel's private runner executor (or the global fallback)
sampleRunnerActiveThreads
Sample a runner pool's live active-thread count into the Tuner primary stati2ptunnel.clientRunner.activeThreads. Per-tunnel pools never go throughgetClientExecutor(), so without this the Tuner only sees the unused shared fallback and cannot grow the budget under load. Safe to call from the accept hot path; no locks.- Parameters:
ex- the pool to sample; ignored if null or shut down
runnerStopped
Deregister a stopped tunnel's runner pool: shut it down (queued burst tasks drain, then workers exit) and redistribute its share of the global budget to the remaining tunnels.- Parameters:
key- the stopped tunnel identity (I2PTunnel or I2PTunnelServer)
getRunnerExecutor
Shared create-or-get for a per-tunnel runner pool. Must run under_runnerPoolLock. Rebalances every live pool so the sum of maxima never exceedsclientRunnerMax.- Parameters:
key- tunnel identity; never nullceiling- this tunnel's per-tunnel max-connections ceiling- Returns:
- non-null executor
rebalanceRunnerPools
private void rebalanceRunnerPools()Rebalance every live runner pool from the global clientRunnerMax budget and each tunnel's ceiling. Must run under_runnerPoolLock. The global budget is a cap on the sum of maxima, so reducing one tunnel's share or removing a tunnel frees budget for the rest.ensureRunnerPoolStats
private void ensureRunnerPoolStats()Register the per-tunnel runner pool rate stats exactly once.rebalanceAllRunnerPools
private void rebalanceAllRunnerPools()Trigger a full runner-pool rebalance after a global budget change.rebalanceRunnerPoolsNow
static void rebalanceRunnerPoolsNow()Trigger a load-aware runner-pool rebalance from the accept hot path after a RejectedExecutionException: an idle sibling may shrink its claim and free budget for this tunnel before the retry. Safe to call with no router instance (no-op).runnerCeilingFor
The runner ceiling for one pool, read now rather than remembered.- Parameters:
h- the pool's state; may be null- Returns:
- the explicit cap, or the Tuner-managed default as it stands now, never below the per-tunnel floor
elevatedRunnerMax
static int elevatedRunnerMax(int currentThreads, int ceiling) How many threads a pool pinned atcurrentwith work queued may be elevated to above its ceiling, and how often the elevation is logged.Both are deliberate: a bounded multiple keeps one tunnel from claiming an unbounded share of the memory budget, and a log interval keeps a tracker under sustained load from writing thousands of identical lines — the earlier saturation warning rotated a 100 MB log file every fifteen minutes.
- Parameters:
currentThreads- the pool's present maximumceiling- the live ceiling for this tunnel- Returns:
- the elevated maximum, or
currentThreadswhen no elevation applies
shouldLogElevation
Whether to log an elevation for this tunnel now. Rate-limited per tunnel, so a sustained burst is reported once per interval rather than once per request.- Parameters:
key- the tunnel identity used as the map keynow- current time in ms- Returns:
- true when the caller should log at WARN
shouldReleaseElevation
static boolean shouldReleaseElevation(long completed, long idleSince, int elevatedMax) Whether an elevation has gone stale and should be released.Pure, so the decay rule can be pinned without a group or a live pool. The rule: a pool elevated to
elevatedMaxgives the shared budget back once it has completed that many tasks since demand last stopped. Work rather than seconds, because the tunnels that get elevated are the ones that accumulate idle observations quickly — a wall-clock interval would be far too generous for a tracker idling between announce batches and far too tight for a browser idling between page loads.- Parameters:
completed- the pool's completed-task count nowidleSince- the completed count when demand last stopped, or 0 if noneelevatedMax- the pool's present floor, or 0 if it was never elevated- Returns:
- true when the floor should be released
shouldElevateRunner
static boolean shouldElevateRunner(int queueDepth, int currentThreads, int ceiling) Whether a pinned pool with this backlog should be elevated past its ceiling.Pure, so the trigger can be pinned without a group or a live pool. It requires the queue to be at least half of
RUNNER_BURST_QUEUE: a submit that finds every worker busy plus one task in flight looks identical to real backlog at that instant, and elevating on that race produces churn — measured live as 256->384 three times in twenty minutes on a tracker, with the decay clawing it back each time.- Parameters:
queueDepth- queued tasks in the poolcurrentThreads- the pool's present maximumceiling- the live ceiling for this tunnel- Returns:
- true when the backlog justifies elevating past the ceiling
noteRunnerIdle
Note that a submit found this pool quiet, and drop a stale elevation if the tunnel has done enough work while quiet to say the burst is over.The rule is expressed in work, not seconds: the floor is released once the pool has completed at least
elevatedMaxtasks since demand last stopped. A wall-clock interval would be arbitrary for a tracker that idles for a second between announce batches and for a browser that idles for a minute between page loads; counting completed work is self-scaling, because the tunnels that get elevated are the ones that can accumulate idle observations quickly. Any further demand throughgrowRunnerExecutorForLoad(Object, int, int)clears the baseline, so a burst with short lulls keeps its headroom.This is why the method is separate from the grow: the caller's fast path is one atomic queue read, and only a pool that was actually elevated pays for the decay check.
- Parameters:
key- the runner-pool key, which is the acceptingI2PTunnelServer
growRunnerExecutorForLoad
Grow one server tunnel's runner pool to match the load it is actually seeing, elevating past the ceiling and warning when demand warrants.Called immediately before a runner submit rather than from the accept loop: the accept loop owns the server handler pool, and a grow there would rebalance on connections that never reach the runner at all. Growing here means the pool is sized at the moment the work is handed to it, and the ceiling is read live, so it also picks up a raised Tuner default.
Grows only — this method never shrinks. Releasing an elevation once the tunnel has gone quiet is
noteRunnerIdle(Object)'s job, and for a different reason: shrinking on a momentary lull would thrash the limit, whereasallowCoreThreadTimeOut(true)means a generous maximum costs no threads while the pool is idle, its workers being reclaimed either way.- Parameters:
key- the runner-pool key, which is the acceptingI2PTunnelServerqueueDepth- queued tasks in the poolactive- busy runners in the pool- Returns:
- the pool's maximum after any growth, or 0 when there is no pool yet
createRunnerExecutor
Create a thread-per-connection runner pool for one tunnel. Core == max so concurrent admits grow workers up to the ceiling before anything queues; a shortRUNNER_BURST_QUEUEabsorbs a micro-burst (browser parallel opens) instead of rejecting to a shed page; overflow still surfaces asRejectedExecutionExceptionto that tunnel's accept path only. Idle threads are reclaimed after the keepalive window viaallowCoreThreadTimeOut.Core must equal max when a real queue is present: with core 0 the ThreadPoolExecutor offers to the queue before creating any worker, so tasks would sit unrun until the queue filled.
- Parameters:
maxThreads- the fixed max worker count for this tunnel's poolindex- an AtomicLong counter used to name worker threads- Returns:
- a never-null executor
resizeRunnerExecutor
Resize a per-tunnel runner pool, keeping core == max so the burst queue is only reached after every worker is busy.- Parameters:
ex- the pool; ignored if null or shut downnewMax- the new max pool size
rebalanceServerExecutors
private void rebalanceServerExecutors()Rebalance every live server-tunnel pool from the global budget, the per-tunnel caps, and the per-tunnel floor. Called on budget/cap changes and on server start/stop; must run under_serverExecutorLock. The global budget is a cap on the sum, so reducing one tunnel's share or removing a tunnel frees budget for the rest.Claims are load-aware: a tunnel with queued or active work claims its full ceiling, while an idle tunnel claims only a base share so a busy sibling (e.g. a tracker under announce flood) receives the budget when the sum of ceilings exceeds the global cap. Under a sufficient budget every tunnel still receives its full ceiling.
growServerExecutorForLoad
Grow one server's handler pool to match the load it is actually seeing.Called from the accept loop before the admission gate, because that is the only place queue depth is visible at the moment it matters.
rebalanceServerExecutors()runs on pool registration and on Tuner parameter changes, not on load, so a pool registered while idle would otherwise stay at its idle size forever. The first version of demand sizing had exactly that: pools created at one thread, refusing connections with an almost-empty queue, because nothing ever asked them to grow.Grows only. Shrinking here would thrash the limit on every lull and buy nothing —
allowCoreThreadTimeOut(true)means a generous limit costs no threads while a pool is quiet, since its workers are reclaimed after the keepalive either way.- Parameters:
server- the accepting server tunnel
rebalanceAll
private void rebalanceAll()Trigger a full handler-pool rebalance after a global or per-tunnel cap change.normalizeThreadOverride
static int normalizeThreadOverride(int v) The per-tunnel thread cap as used bygetServerExecutor(I2PTunnelServer, int)andrebalanceServerExecutors(); a value outside the valid cap range means "follow the Tuner-managed default".allocateServerThreads
static int[] allocateServerThreads(int budget, int[] desired, int floor) Allocate a global server-handler thread budget across the live server tunnels, preserving each tunnel's ceiling (desired) and a shared floor. Pure decision, no router context: lets the Tuner shrink or grow one budget while the per-tunnel pools inherit exactly their share.When the ceilings sum at or below the budget, every tunnel gets its ceiling. Above the budget, the excess is cut proportionally (rounded down, remainder to the last tunnel) so the grand total equals the budget exactly and no tunnel falls below the floor — a genuine per-tunnel cap rather than a free-for-all for the aggregate. When the budget cannot cover a floor for every tunnel, the budget is split evenly with at least 1 thread per live tunnel.
- Parameters:
budget- the global budget; <= 0 yields all zerosdesired- per-tunnel ceilings (any values; clamped internally)floor- the per-tunnel floor; negative treated as 0- Returns:
- per-tunnel allocation, same length as
desired; each entry >= the shared floor whenever the budget allows; the sum equals min(budget, sum of ceilings) except when the budget is smaller than one thread per tunnel
serverThreadsForDemand
static int serverThreadsForDemand(int queueDepth, int active, int cap) Worker threads one server tunnel needs, from its own demand alone.Each server tunnel is sized independently. There is no global budget to divide, because dividing one is what made the busiest eepsite unusable: every open server tunnel was charged a floor first, so with twenty eepsites the floor consumed the budget and the pool actually serving requests was left with a ceiling of about a dozen threads. It then refused connections outright while nineteen idle eepsites held their reservations. Those idle pools were not consuming threads anyway —
allowCoreThreadTimeOut(true)reclaims them after the keepalive — so the budget bought contention and nothing else.Sizing is the load itself: every queued task needs a thread and every busy thread already has one, so
queueDepth + active + headroomis both the minimum that keeps up and the size at which nothing queues. A dormant tunnel sits atSERVER_HANDLER_IDLEand grows on its own demand. The per-tunnel ceiling still applies, so one destination cannot take the whole machine.Pure decision, no router context, so the policy is testable.
- Parameters:
queueDepth- tasks waiting for a handler threadactive- handler threads currently busycap- this tunnel's ceiling (override or Tuner default)- Returns:
- worker threads for this pool, in [1, cap]
scaleDemandsToCeiling
static int[] scaleDemandsToCeiling(int[] demand, int ceiling) Scale per-pool demands down to a global ceiling, proportionally.A safety valve against exhausting the heap, not a budget the pools compete for: each pool is sized by its own demand first, and only if the total is genuinely unsupportable is every pool trimmed together. Trimming is proportional to demand, so the busiest tunnel keeps the largest share and no pool drops below
SERVER_HANDLER_IDLE.Pure, so the allocation policy is testable without a group.
- Parameters:
demand- per-pool thread demand, indexed to match the poolsceiling- total handler threads the JVM can support- Returns:
- per-pool allocation, each in [1, its own demand]
createServerExecutor
Create a bounded executor that runs inbound server-tunnel connection handlers.Handlers are dispatched off the accept thread so a single slow connection cannot stall connection admission. Overflow uses
ThreadPoolExecutor.AbortPolicy: when the fixed worker set and its bounded queue are saturated, the submission throwsRejectedExecutionExceptioninstead of executing the handler inline on the caller. Inline execution (the formerCallerRunsPolicy) would run a write-blocked handler on theI2PTunnelServer.run()accept loop, pinningaccept(), which stops draining the streaming SYN queue and makes every fresh connection attempt expire and be RESET under load. The rejection is surfaced toI2PTunnelServer.run(), which closes the connection but keeps accepting new ones.Exposed package-visible (static) so tests can assert the overflow semantics without a live
I2PAppContext.- Parameters:
threads- the fixed core/max worker countindex- anAtomicLongcounter used to name worker threads- Returns:
- a never-null, bounded executor with
AbortPolicy
createServerExecutor
AscreateServerExecutor(int, AtomicLong)but with an explicit worker keepalive.The production keepalive is
SERVER_KEEPALIVE_MS(30s), which a test cannot wait out: the I2PTunnel suite is run with a 15s per-test timeout, so asserting on core-thread expiry at the real interval always fails on the clock rather than on the behaviour. Injecting a short keepalive lets the test assert the same thing in milliseconds.- Parameters:
threads- the fixed core/max worker countindex- anAtomicLongcounter used to name worker threadskeepAliveMs- idle time before a core thread may be reclaimed- Returns:
- a never-null, bounded executor with
AbortPolicy
resizeServerExecutor
Resize a server-tunnel handler pool, preserving a fixed core == max pool. Called byrebalanceServerExecutors().- Parameters:
ex- the pool; ignored if null or shut downnewThreads- the new fixed size
ensureServerHandlerStats
private void ensureServerHandlerStats()Register the server-handler rate stats exactly once, on first pool use.killServerExecutor
private void killServerExecutor()Shutdown the per-tunnel server handler pools. Queued tasks drain and the worker threads exit; the registry is cleared so a group restart starts fresh pools.killClientExecutor
private void killClientExecutor()Shutdown the client executor and all per-tunnel runner pools, waiting for termination of the shared fallback pool.resizeClientExecutor
void resizeClientExecutor(int newMax) Resize the shared (fallback) client executor max pool size. Called by Tuner for the null-tunnel path; per-tunnel pools are resized byrebalanceAllRunnerPools().- Parameters:
newMax- the new maximum pool size, applied only while the shared executor is running