The signal lives in core because the two layers that need it cannot see each other: apps/streaming is compiled against core and ministreaming only, so it cannot reference a router class, and the router cannot reach into a streaming connection. Core is the one place both already depend on, so the channel is this small static registry.
Keyed by the remote destination only. The streaming layer can identify its peer but not its own local destination — I2PSession does not expose it and I2PAppContext has no accessor — so a local/remote pair key could not be built from the streaming side. A stalled path is a property of the path, not of which local client happened to use it, so the shared key is also the more honest one.
Deliberately tunnel-agnostic: the registry records that a destination stalled and when; the router decides what to do, because tunnel selection is a router concern.
This is a hint, never a veto. A destination whose every tunnel is inside its cooldown must still be sent to, so the registry only answers "was this marked" and the caller is responsible for falling back.
Field Summary
FieldsModifier and TypeFieldDescriptionLast stall time for each peerprivate static final intUpper bound on tracked destinations.private static final longMarks older than this are dropped on the next prune.Constructor Summary
ConstructorsMethod Summary
Modifier and TypeMethodDescriptionstatic voidclear()Drop all marks.static voidmarkStalled(Hash remote, long now) Note that a stream to this destination stopped making progress.private static voidprune(long now) Drop marks that can no longer be in effect for any cooldown a caller may use, so the map stays bounded without discarding a live signal.static booleanrecentlyStalled(Hash remote, long now, long cooldownMs) Whether this destination was marked within the cooldown, meaning a caller may prefer a different outbound tunnel.static intsize()Number of tracked destinations.static longHow long ago this destination was marked, clamped to the cooldown.
Field Details
MAX_ENTRIES
private static final int MAX_ENTRIESUpper bound on tracked destinations. Entries are tiny, but the key space is every destination this router streams to, so the map is capped and pruned rather than allowed to grow with that count.- See Also:
PRUNE_AGE_MS
private static final long PRUNE_AGE_MSMarks older than this are dropped on the next prune. Must exceed the longest cooldown a caller may ask about, or a mark still in effect would be collected.- See Also:
_stalls
Constructor Details
StallRegistry
private StallRegistry()Utility class, not intended for instantiation.
Method Details
markStalled
Note that a stream to this destination stopped making progress.- Parameters:
remote- the remote destination hash; ignored when nullnow- current time in ms
recentlyStalled
Whether this destination was marked within the cooldown, meaning a caller may prefer a different outbound tunnel.- Parameters:
remote- the remote destination hash; ignored when nullnow- current time in mscooldownMs- how long a mark stays in effect; non-positive disables- Returns:
- true if the destination was marked and the mark is still in effect
stallAge
How long ago this destination was marked, clamped to the cooldown.- Parameters:
remote- the remote destination hash; ignored when nullnow- current time in mscooldownMs- how long a mark stays in effect; non-positive disables- Returns:
- elapsed ms since the mark, or
cooldownMswhen unmarked, disabled, or long expired — never less than the cooldown
prune
private static void prune(long now) Drop marks that can no longer be in effect for any cooldown a caller may use, so the map stays bounded without discarding a live signal. A pathological set of marks all younger than the prune age would survive, so the excess is dropped too rather than growing without bound.- Parameters:
now- current time in ms
size
public static int size()Number of tracked destinations. For tests and diagnostics.- Returns:
- current entry count
clear
public static void clear()Drop all marks. For tests.