Repository navigation
fix(net): read each IETF uni stream's type in its own task (backport #5086) - #5125
Conversation
…5086) run_unis waited for each accepted uni stream's type before accepting the next. QUIC opens lower-numbered streams implicitly, so the stream it waited on could be one whose bytes sat behind connection flow control held by later streams' unread data, deadlocking the connection with any IETF peer. Each stream now reads its type and is served in its own child. A stream that breaks the session records the error in a shared slot, which ends the loop as before. Backports only the session.rs half of #5086; the resume.rs half fixes main-only code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Automated review of The adaptation is sound: on drafts 14-17 the subscriber now peeks the first object id, drops a stream whose first id is non-zero (keeping the subscription up), and still treats an empty stream as an empty group. Draft-18+ behavior is unchanged since No blocking issues. Non-blocking:
Verdict: MERGE once CI is green. This is an automated review, not the maintainer's decision |
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 7711e52 (full PR).
No actionable introduced bugs found. Reviewed the complete rs/moq-net/src/ietf/session.rs diff and surrounding task-set, reader, subscriber, and test-transport code, including fatal-error wakeups, duplicate SETUP rejection, early streams, cancellation, and stream-local failures.
Direction: this is a focused backport. Reading each type inside an existing owned child removes the uni-stream head-of-line dependency without introducing runtime-specific spawning. The private Uni helper retains the existing protocol/error policy; no public API or wire-format change. Leaving the unrelated fetch-recovery changes out is appropriate. The silent-stream regression exercises the intended scheduling fix.
Verification limits: static GitHub source review only; I did not compile or rerun tests. At publication, Android passed; Check, Test, and macOS were running, with Windows and WASM queued. The PR's reported local test results were not independently reproduced.
(Written by OpenAI)
Problem
On
release,run_unisinrs/moq-net/src/ietf/session.rswaits for each accepted uni stream's type varint before accepting the next one. QUIC opens lower-numbered streams implicitly, so the stream it waits on can be one whose bytes are still held back by connection flow control. That credit is used up by later streams' unread data, which the loop never gets to. The connection deadlocks. This affects any IETF peer and was seen in about 1 in 50 runs on an impaired link. moq-lite already reads each stream's type in its own child.What was ported
Only the
ietf/session.rshalf of #5086 (da1aa61fb):Uni::serve).kio::Shared<Option<Error>>slot. The accept loop polls that slot and returns the error, so error handling stays as loud as before.ietf::session::tests::a_silent_stream_does_not_hold_up_the_next: a uni stream that never sends its type, followed by a group stream for a retired alias. The second stream must be read and stopped with CANCELLED.Adapted to
releaseAPIs:poll_decode_peekandContext::from_waker(waiter.waker()), and the test uses#[tokio::test(start_paused = true)]like its neighbors.Left out
The
model/resume.rshalf of #5086 (fetchRecovertreatingOld/Evictedas non-verdicts). It fixes code that comes from main-only #4741. The drill and quest changes are left out too.Impact
Tests
releasewithout the fix (the stream behind the silent one is never stopped) and passes with it.ietf::sessiontests pass.just check: lint and clippy clean; 4990 of 4992 workspace tests pass. The 2 failures,moq-tokio::broadcast::broadcast_rejoin_{skips_a_stale,replays_a_current}_warm_cache, run over moq-lite with real sockets and a 10s wall-clock bound. They don't touch this IETF path, and they passed 6 of 6 times when rerun on their own, so they look like load flakes from the full parallel run.(Written by Claude Opus 5.5)
🤖 Generated with Claude Code