A resource that outlives its connection touches nobody - #79
Merged
Conversation
Connection handles are slab slots: noq frees one once its connection is fully drained, and the next connection reuses it. Resources held raw handles and looked entries up per call, so a resource kept past its connection's death answered for -- and acted on -- the slot's next occupant. Observed from wosh as a resume loop: the previous attachment's send-stream resource, dropped at the next attach, fired its drop-implied reset(0) into the reused slot and killed stream 0 of the freshly resumed connection, every time. Every ConnEntry now carries an epoch from a never-reused counter, and everything minted against a connection -- the connection resource, its streams, the dial guard, the accept queue's entries -- is stamped with the epoch it was minted under. Resource-side lookups go through entry_for, which yields the entry only while the slot still holds the SAME connection: a stale resource observes 'gone' (methods fail closed, state() reports closed, wait-closed resolves none, reset/ stop/close drop to no-ops) instead of the stranger in the slot. accept() skips queue entries whose connection died unclaimed, which also stops a reused slot being handed to two acceptors. Pump-side lookups (noq events, datagram dispatch) speak of the current occupant by construction and stay unstamped. Verified by just check and the full cross-host matrix; end to end by wosh's freeze drills (native SIGSTOP repro and the browser gate), which loop forever without this and pass with it. Part of #78 (the conformance case for slot reuse remains open there).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #78 — the epoch guard (the conformance case stays open there).
ConnectionHandleis a slab slot noq reuses once a connection fully drains. Resources held raw handles and looked entries up per call, so a resource kept past its connection's death acted on the slot's next occupant. Observed live (from wosh's freeze drill): the drop-impliedreset(0)of a dead attachment's send stream, held until the next resume attached, landed on the reused slot and killed stream 0 of every freshly resumed connection — a permanent resume loop.The guard:
ConnEntrycarries an epoch from a never-reused counter;connection,send-stream,recv-stream, theDialGuard, the accept queue's entries — is stamped with its mint epoch;entry_for(handle, epoch): a stale resource observes "connection gone" (methods failerror.closed,state()→closed,wait-closed→none,reset/stop/closeno-op) instead of the stranger in the slot;accept()skips queue entries whose connection died unclaimed — which also stops a reused slot being delivered to two acceptors;This also retires every
expect("connection entry")— each was oneconns.removeaway from a guest trap.Verified:
just check(fmt, clippy, validate-wit, tests) and the full cross-hostjust matrix. End to end: wosh's freeze drills — a native SIGSTOP repro and a headless-browser gate — loop forever without this and pass with it (wosh#63 carries the gates).