Repository navigation
fix: refuse worker pins on a closing connection so closeConn cannot free with live pins - #328
Merged
Merged
Conversation
…ree with live pins closeConn fires teardown callbacks (onBody last=true, ws onClose, and the h2 per-stream onRespDrain via h2NotifyClosed) between the pinned-defer guard and the final `doAssert c.totalPins == 0`. On the HTTP/2 sync path a streamed sendFile download whose next chunk read was parked in onRespDrain (backlog at the read-ahead budget, so totalPins was momentarily 0) would, when the connection was torn down, have that parked callback fire dispatchNextRead and acquire a fresh pkFileChunk pin on the very connection being freed. That tripped the assertion and crashed the event loop at first traffic. h1 never hit it because it does not multiplex, so it never parks a per-stream read across a teardown. Set c.closeRequested before firing the teardown callbacks and make acquireDispatchPin (and the h1 ws.blocking connection-pin path) refuse to pin a connection that is already closing. A refused dispatch runs its existing dead-connection cleanup (reclaim the pooled buffer, no-op the send / abort), so the totalPins invariant holds by construction instead of being papered over.
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.
What
Fixes a crash in the HTTP/2 sync event loop where
closeConncould free a connection that still had live worker pins, trippingdoAssert c.totalPins == 0and killing the event loop at first traffic.Root cause
closeConnfires its teardown callbacks (onBody last=true, wsonClose, and the h2 per-streamonRespDrainviah2NotifyClosed) between the pinned-defer guard and the finaldoAssert c.totalPins == 0. On the HTTP/2 sync path, a streamedsendFiledownload whose next-chunk read was parked inonRespDrain(backlog sitting at the read-ahead budget, sototalPinswas momentarily 0) would, when the connection was torn down, have that parked callback firedispatchNextReadand acquire a freshpkFileChunkpin on the very connection being freed. That tripped the assertion.h1 never hit this because it does not multiplex, so it never parks a per-stream read across a teardown. Only h2 stream multiplexing exposes it.
Fix
c.closeRequestedbefore firing the teardown callbacks incloseConn.acquireDispatchPin(and the h1ws.blockingconnection-pin path) refuse to pin a connection that is already closing. A refused dispatch runs its existing dead-connection cleanup (reclaim the pooled buffer, no-op the send / abort), so thetotalPinsinvariant holds by construction rather than being papered over by relaxing the assertion.Changed:
src/vortex/eventloop.nim,src/vortex/request.nim(24 insertions, 3 deletions).How it was found / validated
Surfaced by the
vortex-stressDocker soak matrix: the h2/sync cell crashed instantly on every workload witheventloop.nim(381,3) c.totalPins == 0 connection freed with live worker pins [AssertionDefect]. After the fix, a focused pinned soak (VORTEX_PROTO=h2 VORTEX_SERVER=sync VORTEX_SECONDS=120 nimble stress) reached== stress smoke: all workloads passed ==across all five workloads with all chaos sidecars passing and no recurrence. The fix also held clean for ~53 min under the full parallel matrix before an unrelated host-contention/OOM event (not a vortex defect) ended that run.