java.lang.Object
java.lang.Thread
net.i2p.util.I2PThread
net.i2p.router.transport.crypto.X25519KeyFactory
- All Implemented Interfaces:
Runnable, KeyFactory
Try to keep DH pairs at the ready. It's important to do this in a separate thread, because if we run out, the pairs are generated in the NTCP Pumper thread, and it can fall behind.
Pool sizes scale dynamically based on memory headroom, CPU pressure, and connection demand. The Tuner calls refreshPoolSize() every 15 seconds to adapt to changing conditions.
Nested Class Summary
Nested classes/interfaces inherited from class I2PThread
I2PThread.OOMEventListenerModifier and TypeClassDescriptionstatic interfaceListener for out-of-memory events.Nested classes/interfaces inherited from class Thread
Thread.Builder, Thread.State, Thread.UncaughtExceptionHandlerField Summary
FieldsModifier and TypeFieldDescriptionprivate intDelay between the key calculations, in ms.private longInterval of the settings recheck.private final I2PAppContextThe app context.private final AtomicIntegerEmpties observed since last refresh — drives pool growth.private booleanTrue while the factory thread runs.private final LinkedBlockingQueue<KeyPair> The precalculated key pairs.private final LogLogger for the key factory.private intLargest key queue allowed.private intSmallest key queue allowed.private intKeys we stop calculating above.private intKeys we want in the queue at least.private final AtomicIntegerKey consumption count since last refresh — drives fill ceiling.private static final intDefault key calculation delay, in ms.private static final intAbsolute ceiling — never exceed this regardless of headroom.private static final intAbsolute floor — never go below this even under memory pressure.private static final intEach keypair is ~64 bytes; this is the memory budget per key in bytes.private static X25519KeyFactoryThe most recently created factory, reused on reconfigure.private static final StringConfig key for the key calculation delay.Fields inherited from class Thread
MAX_PRIORITY, MIN_PRIORITY, NORM_PRIORITYConstructor Summary
ConstructorsMethod Summary
Modifier and TypeMethodDescriptionprivate final booleanAdds a precomputed key pair to the pool.private static intclampSmooth(int prev, int target, int factor) Clamp a value to within a factor of the previous value.(package private) voidDynamically compute target pool sizes based on real-time system signals.static X25519KeyFactoryThe most recently constructed factory.getKeys()Pulls a prebuilt keypair from the queue, or if not available, construct a new one.intUpper bound on the precalculated key queue size.intLower bound on the precalculated key queue size.intgetSize()Returns the current number of precalc keys queued.private KeyPairprecalc()Calculates one key pair.voidRecord an empty-queue event for demand tracking.voidCalled by the Tuner every 15 seconds.voidreturnUnused(KeyPair kp) Return an unused DH key builder to be put back onto the queue for reuse.voidrun()Precalculate keys until shutdown.private voidrun2()Fills the key queue in the background.voidsetMaxSize(int max) The maximum precalc queue size.voidsetMinSize(int min) The minimum precalc queue size.voidshutdown()Note that this stops the singleton precalc thread.Methods inherited from class I2PThread
addOOMEventListener, fireOOM, removeOOMEventListener, startModifier and TypeMethodDescriptionstatic voidRegister a new component that wants notification of OOM events.protected voidfireOOM(OutOfMemoryError oom) Notify listeners that an OutOfMemoryError occurred.static voidUnregister a component that wants notification of OOM events.voidstart()Overridden to provide useful info to users on OOM, and to prevent shutting down the whole JVM for what is most likely not a heap issue.Methods inherited from class Thread
activeCount, clone, currentThread, dumpStack, enumerate, getAllStackTraces, getContextClassLoader, getDefaultUncaughtExceptionHandler, getName, getPriority, getStackTrace, getState, getThreadGroup, getUncaughtExceptionHandler, holdsLock, interrupt, interrupted, isAlive, isDaemon, isInterrupted, isVirtual, join, join, join, join, ofPlatform, ofVirtual, onSpinWait, setContextClassLoader, setDaemon, setDefaultUncaughtExceptionHandler, setName, setPriority, setUncaughtExceptionHandler, sleep, sleep, sleep, startVirtualThread, threadId, toString, yield
Field Details
lastInstance
The most recently created factory, reused on reconfigure._context
The app context._log
Logger for the key factory._minSize
private volatile int _minSizeSmallest key queue allowed._maxSize
private volatile int _maxSizeLargest key queue allowed._calcDelay
private volatile int _calcDelayDelay between the key calculations, in ms._targetMin
private volatile int _targetMinKeys we want in the queue at least._targetMax
private volatile int _targetMaxKeys we stop calculating above._keys
The precalculated key pairs._isRunning
private volatile boolean _isRunningTrue while the factory thread runs._checkDelay
private long _checkDelayInterval of the settings recheck._emptyCount
Empties observed since last refresh — drives pool growth._usedCount
Key consumption count since last refresh — drives fill ceiling.PROP_DH_PRECALC_DELAY
Config key for the key calculation delay.- See Also:
DEFAULT_DH_PRECALC_DELAY
private static final int DEFAULT_DH_PRECALC_DELAYDefault key calculation delay, in ms.- See Also:
HARD_MIN
private static final int HARD_MINAbsolute floor — never go below this even under memory pressure.- See Also:
HARD_MAX
private static final int HARD_MAXAbsolute ceiling — never exceed this regardless of headroom.- See Also:
KEY_SIZE_BYTES
private static final int KEY_SIZE_BYTESEach keypair is ~64 bytes; this is the memory budget per key in bytes.- See Also:
Constructor Details
X25519KeyFactory
X25519KeyFactory.- Parameters:
ctx- the context supplying the log, stat manager and RNG for key generation
Method Details
computeTargetSizes
void computeTargetSizes()Dynamically compute target pool sizes based on real-time system signals.Signals used:
- Free memory headroom — more free memory = larger pool budget
- Memory pressure — high usage = shrink aggressively
- CPU load (job lag) — busy CPU = limit generation rate
- Empty queue events — zero empties = low demand shrinks pool
- Active peers — more connections = more key demand
Memory budget: up to 2% of free heap when active, 0.5% when idle.
clampSmooth
private static int clampSmooth(int prev, int target, int factor) Clamp a value to within a factor of the previous value. Prevents wild swings between refresh cycles.- Parameters:
prev- the previous cycle's value, center of the allowed rangetarget- the desired new value to clampfactor- maximum multiplicative change allowed in either direction- Returns:
- target clamped to prev/factor - prev*factor and to the hard limits
refreshPoolSize
public void refreshPoolSize()Called by the Tuner every 15 seconds. Manages fill ceiling (_maxSize) based on demand and memory pressure. Does NOT override the Tuner-set _minSize — the Tuner owns that value viasetMinSize(int).recordEmpty
public void recordEmpty()Record an empty-queue event for demand tracking. Called bygetKeys()when the pool is depleted.getInstance
The most recently constructed factory.- Returns:
- the most recently constructed factory, or null if none has been created yet
getMinSize
public int getMinSize()Lower bound on the precalculated key queue size.- Returns:
- the min size
setMinSize
public void setMinSize(int min) The minimum precalc queue size.- Parameters:
min- the requested floor on queued keys, raised to HARD_MIN if lower
getMaxSize
public int getMaxSize()Upper bound on the precalculated key queue size.- Returns:
- the max size
setMaxSize
public void setMaxSize(int max) The maximum precalc queue size.- Parameters:
max- the requested ceiling on queued keys, raised to the current minimum
getSize
public int getSize()Returns the current number of precalc keys queued.- Returns:
- how many prebuilt keypairs are waiting to be handed out, which counts keypairs rather than bytes and may be zero
shutdown
public void shutdown()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. SeeRouter.shutdown(int).run
run2
private void run2()Fills the key queue in the background.getKeys
Pulls a prebuilt keypair from the queue, or if not available, construct a new one.- Specified by:
getKeysin interfaceKeyFactory- Returns:
- an X25519 keypair for local ephemeral Diffie-Hellman use, either taken from the precalculation queue or newly generated
precalc
returnUnused
Return an unused DH key builder to be put back onto the queue for reuse.- Parameters:
kp- the key pair taken from the pool but never consumed on the wire
addKeys
Adds a precomputed key pair to the pool.- Parameters:
kp- the key pair to offer to the queue- Returns:
- true if successful, false if at or above max size