quic: improve stream cleanup & lookup - #65944
Open
pimterry wants to merge 1 commit into
Open
Conversation
Collaborator
|
Review requested:
|
Signed-off-by: Tim Perry <pimterry@gmail.com>
pimterry
force-pushed
the
nghttp3-stream-caching
branch
from
September 9, 2026 17:10
6d51d4e to
28daf59
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #65944 +/- ##
==========================================
+ Coverage 90.16% 90.18% +0.01%
==========================================
Files 771 771
Lines 265434 265445 +11
Branches 50450 50457 +7
==========================================
+ Hits 239332 239395 +63
+ Misses 17045 16993 -52
Partials 9057 9057 🚀 New features to boost your workflow:
|
jasnell
reviewed
Sep 9, 2026
| Stream* stream, | ||
| QuicError&& error) { | ||
| DCHECK_NOT_NULL(stream); | ||
| if (stream == nullptr) return; |
Member
There was a problem hiding this comment.
Nit: let's add a comment here explaining briefly the conditions in whch stream can be nullptr
jasnell
reviewed
Sep 9, 2026
| if (flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET) { | ||
| session->application().ReceiveStreamClose( | ||
| stream, QuicError::ForApplication(app_error_code)); | ||
| stream_id, stream, QuicError::ForApplication(app_error_code)); |
Member
There was a problem hiding this comment.
A comment here explaining that we're passing stream_id independently from stream because stream might be nullptr would be good.
jasnell
approved these changes
Sep 9, 2026
jasnell
left a comment
Member
There was a problem hiding this comment.
LGTM with a couple of comment nits
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.
Extracted from #63995, but with a few further improvements en route.
The core change here is to stop querying for our
Streaminstance by id in everyon_read_data,on_acked_stream_dataandon_receive_datacall (all hot paths where this is awkward & expensive). We now cache it in nghttp3's stream-linked data, which it then passes back to each callback for us, so we can just read it directly.Note that both ngtcp2 & nghttp3 separately track per-stream user data, which is a bit confusing for some of the explanation below - we're caching this in nghttp3 user data.
Having extracted that though and working through the logic to clean up when H3 streams close, I found some interesting problems, so this expanded a bit:
&streamon every nghttp3 client stream (the last argument tonghttp3_conn_submit_request) but we never read it or never cleaned it up - this was a dangling pointer that's only safe because it was unused.stream.destroy()from JS calledSession::RemoveStreamwhich cleared ngtcp2 stream user data beforehand, and thenSession::on_stream_closeexited early if the ngtcp2 user data was null. That means we skipped callingReceiveStreamClose, which means we never callednghttp3_conn_close_stream2, which is what actually frees the nghttp3 stream. End result is that locally destroyed nghttp3 streams were orphaned & retained for the lifetime of the entire connection.Http3ApplicationImpl::OnStreamClosecalledExtendMaxStreams, and thenSession::RemoveStreamcalledngtcp2_conn_extend_max_streams_*as well, so closing any remote stream actually expanded the max-streams window twice.Http3ApplicationImpl::OnStreamClosecalledExtendMaxStreamswithREMOTEunconditionally, even though it was called for local streams too, so closing a local stream expanded the remote stream window as well.End result: we had a few different & overlapping paths that stream closure could take, and the interactions created a selection of small bugs.
Src changes here are a net deletion, simplifying this and fixing the bugs:
Session:on_stream_closecallsapplication().ReceiveStreamClose(), and the application cleans up its state there in one place.Session::RemoveStreamcleans up that side, and callsapplication.StreamRemoved()to inform the app when it's gone.ReceiveStreamClose, so we just cleanup in a single step there instead.on_stream_closefires, the credit is returned immediately.ExtendMaxStreams,EndpointLabel) goes away completely.