Repository navigation
refactor(quic)!: implement native WebTransport poll transports - #7771
Conversation
Quest-Claim: 547c0cc827367560
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Apply the local reviewer’s comment move unchanged. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 27 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. |
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed head fd5d25e.
-
[P2] Keep the close probe from discarding unread leases — rs/moq-tokio/src/quic/driver/recv.rs:235–241. A lease moves bytes out of the assembler but retains unread bytes separately. If a payload and FIN are leased and the lease is dropped unread, this ordinary zero-byte Chunks read sees the assembler at FIN and frees the stream, discarding the retained payload.
closed()returns success although data is unread, and a later lease on the live connection can panic. Observe FIN without a plain read that frees a leased stream; add a payload+FIN, dropped-unread-lease, close, re-lease regression. -
[P2] Release canceled partial opens — rs/moq-tokio/src/quic/web/session.rs:314–336. The pending send/recv pair lives in a session-wide slot rather than the
open_bifuture. Cancel an open while its WebTransport header is blocked by flow control: the pair still holds a bidi stream credit. With one credit,poll_open_requestuses a different slot and keeps returning None even after the pressure clears, until another ordinary open polls the abandoned pair or the session drops. The previous direct async open dropped its local streams on cancellation. Tie the partial open to its operation or arrange cancellation cleanup, and test this cross-kind case. -
[P2] Preserve a cached reset in the close result — rs/moq-tokio/src/quic/driver/recv.rs:233–245. After a read observes RESET_STREAM,
poll_read_genericsets bothresetandall_data_read; the nextclosed()returns success at the all-data-read check before seeing the reset. Two close calls likewise produce error then success. Check the cached reset first, as the io_uring implementation does, and cover both orders.
Verification: static GitHub review of the two drivers, shared stream-credit logic, transport contracts and tests. I did not run tests, exercise cancellation or reproduce these cases.
There was a problem hiding this comment.
Important
A few correctness issues in the new terminal stream paths, plus one uring datagram behavior change, are worth a look before merge.
Reviewed changes
- Native poll implementations.
moq-tokioandmoq-uringquic::websessions now implementmoq_net::transport::polldirectly; the generic adapters,Grouped/GroupedSend,poll_op, and theweb-transport-traitdependency are gone. - Aligned stream and handshake API. Role-local
send::Stream/recv::Stream, consumingfinish/reset/stop,send::Finished,Requestderef toproto::ConnectRequest, typed CONNECT responses, andconnection()accessors. - io_uring header sweep. Partial headers move to
FuturesUnordered, critical streams use three role slots, and the handshake stream cap is removed; freed stream credit is returned once the peer spends all advertised streams. - Stream-credit fix.
queue_max_stream_idno longer withholds returned credit below the batch threshold when the peer has used every advertised stream. - Tests, benchmarks, docs, quests.
web_headersnightly bench/test, native transport allocation bench, rejection and clean-FIN cases, migration notes, and documentation links.
The stream-credit condition is safe (advertised value matches enforcement and stays monotonic), and I could not find a lost wake or stream leak in the shared Opening/SessionAccept state. The findings below are in the new terminal paths.
ℹ️ Quest audit still names the deleted transport adapter
The dependent quest prose in net-wrap, quic-lock-contention, relay-allocs, and quic-sansio-driver was updated, but quest/m2/audit-compile-time.md:291-296 still measures moq_tokio::transport::Session<quic::web::Session>, a type this PR deletes. Annotate the historical measurement or point it at the native implementation so the audit does not describe code that no longer compiles.
Technical details
# Stale audit reference
## Affected sites
- `quest/m2/audit-compile-time.md:291-296` — transport-rows table still names
`moq_tokio::transport::Session<quic::web::Session>` and
`moq_tokio::transport::Session`.
## Required outcome
- Either note that the measurement predates #7771 and the adapter no longer
exists, or replace the reference with the native `quic::web::Session`.deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏
|
quest land watch: the merge queue removed this pull request at fd5d25e because its The land fixer works on it in the background, and the watcher re-enqueues it at the back of the queue once it is fixed or judged flaky, or hands it back here with what it needs.
…documented under each public name [1.12ms]
moq-doc test: (pass) a rendered workspace > a namespace export keeps its public name [1.08ms]
moq-doc test: (pass) a rendered workspace > a hidden declaration is documented under the alias that re-exports it [0.97ms]
moq-doc test: �[93m[warning]�[0m Value, defined in @test/hidden/src/access.ts, is referenced by read but not included in the documentation
moq-doc test: �[93m[warning]�[0m Value, defined in @test/hidden/src/access.ts, is referenced by read but not included in the documentation
moq-doc test: (pass) an unexported declaration that a public one names fails the run [383.81ms]
moq-doc test: �[93m[warning]�[0m Failed to resolve link to "Missing" in comment for value
moq-doc test: (pass) a link that does not resolve fails the run [184.63ms]
moq-doc test:
moq-doc test: ::endgroup::
moq-doc test:
moq-doc test: ::group::concept/audio-jitter/corpus.test.ts:
moq-doc test: (pass) audio jitter corpus > paced > matches the checked-in file [0.31ms]
moq-doc test: (pass) audio jitter corpus > paced > is a trace of equal-length clocks [0.03ms]
moq-doc test: (pass) audio jitter corpus > paced > arrives in a monotonic order [0.93ms]
moq-doc test: (pass) audio jitter corpus > paced > names a target for the first frame [0.03ms]
moq-doc test: (pass) audio jitter corpus > paced > reports every target as a change [0.06ms]
moq-doc test: (pass) audio jitter corpus > flush > matches the checked-in file [0.03ms]
moq-doc test: (pass) audio jitter corpus > flush > is a trace of equal-length clocks
moq-doc test: (pass) audio jitter corpus > flush > arrives in a monotonic order [0.22ms]
moq-doc test: (pass) audio jitter corpus > flush > names a target for the first frame
moq-doc test: (pass) audio jitter corpus > flush > reports every target as a change
moq-doc test: (pass) audio jitter corpus > advertised > matches the checked-in file [0.03ms]
moq-doc test: (pass) audio jitter corpus > advertised > is a trace of equal-length clocks
moq-doc test: (pass) audio jitter corpus > advertised > arrives in a monotonic order [0.21ms]
moq-doc test: (pass) audio jitter corpus > advertised > names a target for the first frame
moq-doc test: (pass) audio jitter corpus > advertised > reports every target as a change
moq-doc test: (pass) audio jitter corpus > buildup > matches the checked-in file [0.04ms]
moq-doc test: (pass) audio jitter corpus > buildup > is a trace of equal-length clocks
moq-doc test: (pass) audio jitter corpus > buildup > arrives in a monotonic order [0.25ms]
moq-doc test: (pass) audio jitter corpus > buildup > names a target for the first frame
moq-doc test: (pass) audio jitter corpus > buildup > reports every target as a change [0.01ms]
moq-doc test: (pass) audio jitter corpus > reorder > matches the checked-in file [0.02ms]
moq-doc test: (pass) audio jitter corpus > reorder > is a trace of equal-length clocks
moq-doc test: (pass) audio jitter corpus > reorder > arrives in a monotonic order [0.17ms]
moq-doc test: (pass) audio jitter corpus > reorder > names a target for the first frame
moq-doc test: (pass) audio jitter corpus > reorder > reports every target as a change
moq-doc test: (pass) audio jitter corpus > backlog > matches the checked-in file [0.04ms]
moq-doc test: (pass) audio jitter corpus > backlog > is a trace of equal-length clocks
moq-doc test: (pass) audio jitter corpus > backlog > arrives in a monotonic order [0.36ms]
moq-doc test: (pass) audio jitter corpus > backlog > names a target for the first frame
moq-doc test: (pass) audio jitter corpus > backlog > reports every target as a change
moq-doc test: (pass) audio jitter corpus > idle > matches the checked-in file [0.03ms]
moq-doc test: (pass) audio jitter corpus > idle > is a trace of equal-length clocks
moq-doc test: (pass) audio jitter corpus > idle > arrives in a monotonic order [0.18ms]
moq-doc test: (pass) audio jitter corpus > idle > names a target for the first frame
moq-doc test: (pass) audio jitter corpus > idle > reports every target as a change
moq-doc test: (pass) audio jitter corpus > tunein > matches the checked-in file [0.04ms]
moq-doc test: (pass) audio jitter corpus > tunein > is a trace of equal-length clocks
moq-doc test: (pass) audio jitter corpus > tunein > arrives in a monotonic order [0.32ms]
moq-doc test: (pass) audio jitter corpus > tunein > names a target for the first frame
moq-doc test: (pass) audio jitter corpus > tunein > reports every target as a change
moq-doc test: (pass) audio jitter corpus > spike > matches the checked-in file [0.06ms]
moq-doc test: (pass) audio jitter corpus > spike > is a trace of equal-length clocks
moq-doc test: (pass) audio jitter corpus > spike > arrives in a monotonic order [0.24ms]
moq-doc test: (pass) audio jitter corpus > spike > names a target for the first frame
moq-doc test: (pass) audio jitter corpus > spike > reports every target as a change
moq-doc test: (pass) audio jitter corpus > straggler > matches the checked-in file [0.04ms]
moq-doc test: (pass) audio jitter corpus > straggler > is a trace of equal-length clocks
moq-doc test: (pass) audio jitter corpus > straggler > arrives in a monotonic order [0.17ms]
moq-doc test: (pass) audio jitter corpus > straggler > names a target for the first frame
moq-doc test: (pass) audio jitter corpus > straggler > reports every target as a change
moq-doc test: (pass) audio jitter corpus > fixed > matches the checked-in file [0.01ms]
moq-doc test: (pass) audio jitter corpus > fixed > is a trace of equal-length clocks
moq-doc test: (pass) audio jitter corpus > fixed > arrives in a monotonic order [0.07ms]
moq-doc test: (pass) audio jitter corpus > fixed > names a target for the first frame
moq-doc test: (pass) audio jitter corpus > fixed > reports every target as a change
moq-doc test:
moq-doc test: ::endgroup::
moq-doc test:
moq-doc test: 134 pass
moq-doc test: 0 fail
moq-doc test: 15964 expect() calls
moq-doc test: Ran 134 tests across 3 files. [3.41s]
moq-doc test: Exited with code 0
js: test finished in 27.70s
##[endgroup]
##[group]check markdown
[0.04ms] ".env"
bun install v1.4.2 (744846f84)
Resolving dependencies
Resolved, downloaded and extracted [1]
$ bun run --cwd js/libav build
$ bun build.ts
warning: ignoring untrusted flake configuration setting 'extra-substituters'.
Pass '--accept-flake-config' to trust it
warning: ignoring untrusted flake configuration setting 'extra-trusted-public-keys'.
Pass '--accept-flake-config' to trust it
warning: ignoring untrusted flake configuration setting 'fallback'.
Pass '--accept-flake-config' to trust it
Checked 771 installs across 848 packages (no changes) [258.00ms]
##[endgroup]
##[group]check gh
python3 sh/version.py
shared version: disabled until the release cut runs just version
actionlint
zizmor --offline --quiet .github
�[33m WARN�[0m �[2mzizmor::registry::input�[0m�[2m:�[0m failed to validate file://.github/workflows/dependabot.yml as dependabot config: input does not match expected validation schema
�[32mNo findings to report. Good job!�[39m (�[1m�[93m28�[39m ignored, �[93m114�[39m suppressed�[0m)
sh/gh/alert.sh check-coverage
alert.yml watches all 34 alertable workflows
bun test sh/gh/alert.test.ts
bun test v1.4.2 (744846f84)
##[group]sh/gh/alert.test.ts:
(pass) Discord alert policy for pull_request [0.20ms]
(pass) Discord alert policy for pull_request_target [0.12ms]
(pass) Discord alert policy for push [0.09ms]
(pass) Discord alert policy for schedule [0.10ms]
(pass) Discord alert policy for workflow_dispatch [0.04ms]
(pass) Discord alert policy for workflow_run [0.02ms]
(pass) cron workflows only alert on scheduled failures [8.38ms]
(pass) ordinary CI never subscribes to Discord alerts [0.13ms]
(pass) alert coverage excludes CI-only workflows and retains release and nightly workflows [39.57ms]
##[endgroup]
9 pass
0 fail
233 expect() calls
Ran 9 tests across 1 file. [86.00ms]
bun sh/gh/check-runs.ts
python3 sh/gh/test-inventory.py
python3 bench/relay.test.py
......
----------------------------------------------------------------------
Ran 6 tests in 1.512s
OK
##[endgroup]
##[group]test gh
python3 sh/version.test.py
......error: recipe `test` was terminated on line 37 by signal 15
##[endgroup]
##[error]ci: test rs failed
##[error]Process completed with exit code 100.
##[group]Run actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9
with:
name: cargo-timings
path: target/cargo-timings/cargo-timing-*.html
if-no-files-found: ignore
retention-days: 14
compression-level: 6
overwrite: false
include-hidden-files: false
archive: true
env:
BASE: 55d5a14fbac345e11237ed74183865b73f12304f
CHECK_FAIL_FAST: 1
NEXTEST_TEST_THREADS: 24
MBX_CACHE_LINKS: 1
MBX_REMOTE_URL:
##[endgroup]
With the provided path, there will be 5 files uploaded
Artifact name is valid!
Root directory input is valid!
Uploading artifact: cargo-timings.zip
Beginning upload of artifact content to blob storage
Uploaded bytes 254437
Finished uploading artifact content to blob storage!
SHA256 digest of uploaded artifact is 65e9486be61f049948f70315b1cf427fecf52bab7a20e3be3e0065bf3daa74a7
Finalizing artifact upload
Artifact cargo-timings successfully finalized. Artifact ID 13286657576
Artifact cargo-timings has been successfully uploaded! Final size is 254437 bytes. Artifact ID is 13286657576
Artifact download URL: https://github.com/moq-dev/moq/actions/runs/41752982583/artifacts/13286657576
Post job cleanup.
##[start-action display=Run jdx/mr-boxington-action@88acf2bb50552c7c508cbac13e5f8c576b95b1de;id=__6137ba0d-327f-4b0c-a407-4bb49433070b.mbx]
##[end-action id=__6137ba0d-327f-4b0c-a407-4bb49433070b.mbx;outcome=skipped;conclusion=skipped;duration_ms=0]
Post job cleanup.
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git version
git version 2.55.0
Temporarily overriding HOME='/var/cache/github-runner-work/_temp/eceae63f-11c4-45c8-be25-7ef00653cedb' before making global git config changes
Adding repository directory to the temporary git global config as a safe directory
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git config --global --add safe.directory /var/cache/github-runner-work/moq/moq
Removing SSH command configuration
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git config --local --name-only --get-regexp core\.sshCommand
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
Removing HTTP extra header
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git config --local --name-only --get-regexp http\.https\:\/\/github\.com\/\.extraheader
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.https\:\/\/github\.com\/\.extraheader' && git config --local --unset-all 'http.https://github.com/.extraheader' || :"
Removing includeIf entries pointing to credentials config files
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
A job completed hook has been configured by the self-hosted runner administrator
##[group]Run '/nix/store/gx2v6q84b0w43bafm0whkdx93qyxbpps-ci-cache-gc.sh/bin/ci-cache-gc.sh'
shell: /nix/store/1mv3qz005gkbalxfghk4y3s8ayv4d2dl-bash-interactive-5.3p15/bin/bash --noprofile --norc -e -o pipefail {0}
##[endgroup]
evicted 0 objects and 0 action results (0 B logical); 50.0 GiB logical remain
evicted 0 objects and 0 action results (0 B logical); 50.0 GiB logical remain
removed 1 target directories (34.6 GiB logical, 0 abandoned and 1 live); 0 B logical remain
Cleaning up orphan processes
(written by quest land watch) |
|
codex / Codex land fixer note:
(written by codex / Codex) |
|
codex / Codex land fixer: thread 'moq-ffi-worker' (1035695) panicked at rs/moq-ffi/src/ffi.rs:558:17:
injected origin driver panic
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'origin::supervision_tests::a_panicked_origin_task_reaches_the_host_while_another_origin_keeps_working' (1035639) panicked at rs/moq-ffi/src/origin.rs:688:9:
assertion failed: matches!(failed.next().await, Err(MoqError::Task { .. }))
(written by codex / Codex) |
# Conflicts: # Cargo.lock # rs/moq-tokio/MIGRATE.md
…ebTransport - Tokio send reset reads the driver-recorded stop after the reset again, so a STOP_SENDING racing the reset still reports its code. - Tokio's receive close watch returns a cached reset instead of a clean end. - moq-quic does not report the end of a finished stream while leased bytes are unread, so a close watch no longer discards them. - Both stacks drop a datagram that names no session, or another one, instead of failing the session. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e driver Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # Cargo.lock # rs/moq-relay/Cargo.toml
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
Changes since the prior pullfrog review at fd5d25e:
- Fixed the tokio reset/stop race:
reset_muttakesstream.stopped()before the reset again and resolves it after, so aSTOP_SENDINGthat races the local reset still reports its code. - Fixed the cached reset close watch: the tokio driver's
recv::poll_closedreturnsself.reader.resetbefore the FIN read path, keeping repeatedclosed()calls a reset. - Fixed the unread-lease FIN: moq-quic's
Chunks::nextstays blocked whileLeased::has_unread(), so a finished stream no longer frees and discards leased bytes. Applies to both drivers and qmux. - Fixed stray datagrams: both WebTransport stacks now drop a datagram that names no session or another session instead of ending the session, with tokio and io_uring regression tests.
- Added the ambient-clock timeout to the stray-datagram test and aligned io_uring's reset-watch/reset-stop comments with the driver.
- Merged
origin/maintwice, resolving onlyCargo.lockandrs/moq-tokio/MIGRATE.md(first) andCargo.lock/rs/moq-relay/Cargo.toml(second) by hand.
deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed head 24e2220, following fd5d25e.
The unread-lease and cached-reset findings are addressed: FIN no longer frees a stream while leased bytes remain unread, and the close watch checks the cached reset before reporting a clean end. The added regressions cover these paths. I found no additional actionable issue in this update.
Correction to my earlier canceled-open comparison: the old moq-net adapter also held an in-flight operation in a session slot, so that part was not new for adapter users. Direct native Session::open_bi previously held its partially opened streams in the future, whereas the new wrapper retains a canceled partial open in the session slot until adopted or the session closes. I have not established a further failure beyond that retained credit for a direct native caller.
Verification limits: GitHub-only static review of the delta, stream lifetime and tests, with an independent pass. No tests or runtime cancellation scenarios were run.
(Written by review (OpenAI))
|
quest land watch: the merge queue removed this pull request at 24e2220 because it conflicts with The land fixer works on it in the background, and the watcher re-enqueues it at the back of the queue once it is fixed or judged flaky, or hands it back here with what it needs. (written by quest land watch) |
|
codex / Codex land fixer note:
(written by codex / Codex) |
|
codex / Codex land fixer: Auto-merging quest/m2/net-wrap/README.md CONFLICT (content): Merge conflict in quest/m2/net-wrap/README.md Auto-merging rs/moq-tokio/src/client.rs Automatic merge failed; fix conflicts and then commit the result. (written by codex / Codex) |
# Conflicts: # quest/m2/net-wrap/README.md

Problem
Native QUIC sessions passed through
web_transport_traitadapters that boxed operations, copied writes/datagrams, and emulated terminal waits. The driver-poll prerequisite (moq-dev/moq#7707) is merged; those adapters still remained. io_uring also rescanned every partial stream header whenever any stream progressed.Approach
poll_op, andOpSlot.send::Stream,recv::Stream, consuming terminal methods,send::Finished, protocol CONNECT types, and explicitconnection()access. Documentquic::webas a supported library in both crates.FuturesUnordered; partial/application streams retain their stream credit, and three role slots retain HTTP/3 critical streams.Impact
quic::web::Sessiondirectly to moq-net. Remove generictransport::Session<S>/send/receive/error adapters,Grouped/GroupedSend, and allweb_transport_traitimplementations. Custom transports implementmoq_net::transport::polldirectly.Local::new(transport::Session::new(session, address))withLocal::new(session). The native session directly implements moq-net's poll traits; the generic adapters andweb_transport_traitimplementations are removed.finish(self)returnssend::Finished, whilereset(self, code)/stop(self, code)consume their stream. Session polls take&self,&kio::Waiter, and driver send options. Stream IDs useid(). Datagrams acceptimpl Buf.stats()exposes the driver snapshot; use moq-netmetrics()for transport metrics.proto::ConnectRequest; useconnection(),proto::ConnectResponse, andhttp::StatusCode.respondrequires OK;rejectrequires 4xx/5xx. Tokio removes Session'sDeref, theweb::Serverhelper, andRequest::connect(). io_uring removesResponse/Rejected. Both crates have migration notes underUnreleased.closed()without a read; buffered payload, including leased bytes handed back unread, must be read first. A datagram that names no session or another session is dropped rather than failing the session (Tokio failed it before this PR as well).poll_resetremains a failure-only wait. Header-write stops preserve valid application codes, and invalid stream codes remain typed failures. Raw application error codes outside u32 are not advertised as a valid moq-net registry code.web-transport-trait; qmux retains its independent interop dev-dependency.MAX_STREAMSbelow the batching threshold once all advertised stream IDs are used, preventing held partial headers from freezing unrelated stream turnover. A read no longer reports the end of a finished stream while leased bytes are unread. No public API change.MAX_STREAMScan be sent sooner. No package version bumps.Alternatives
Keeping the generic adapters would retain the boxing/copies after the drivers gained native polls. A cap on incomplete headers only refuses peers; retaining stream credit bounds their storage by negotiated flow control. Re-polling a vector still costs a scan on every ready stream, so io_uring now queues only new/woken headers.
The stream-credit fix also applies to both drivers and QMux through the shared engine. Upstream retains the same batch-only condition; it was checked before fixing the local source.
The aligned API covers the operations both runtimes already support. Tokio retains its existing client CONNECT helper; io_uring's existing WebTransport handshake remains server-side. Runtime-specific connection controls remain accessible through
connection().Measurements
Base:
d1bcd5748b5565abae7b8d95885dd4b3cf04cbe7(driver polls merged). Native implementation measured at20e94509dd9a2507614a25d82b96751b9a35abec, including the stream-credit fix. The later review fixes validate local rejection statuses and map header-write failures; neither path runs in these successful-session workloads.Rust 1.98.1 in the pinned Nix shell, Linux x86_64, optimized
profilingprofile. Both revisions use the same workload and current generator. Relay profiles pin CPUs 0-7 and use one runtime worker. Available SSH hosts were busy or unavailable, and this host had concurrent builds, so these are diagnostic pairs, not a CPU-capacity or statistically isolated improvement claim.Relay:
just bench-profile heap fanout 1 <tokio-workers|io-uring-workers> d1bcd5748b5565abae7b8d95885dd4b3cf04cbe7. One publisher, 64 subscribers, 10 fps, 400-byte frames, one frame per group stream. Every run delivered its offered load with zero reported loss. CPU is measured in the unprofiled control and normalized by delivered application payload; allocation estimates come from the separate sampled jemalloc steady window and include all relay work, divided by delivered streams.Tokio's paired relay allocation estimate and churn drop; io_uring's CPU/churn are roughly flat and its sampled allocation count is slightly higher. The heavier 256-subscriber, 60 fps/1200-byte diagnostic lost substantial Tokio traffic under concurrent host load, so it does not establish a paired performance improvement.
Native transport: identical
fanoutallocation-counter instrumentation on both revisions, with the base using its old adapter.NATIVE_ALLOCS=1 cargo bench --locked --profile profiling -p moq-tokio --bench fanout -- native_transport. A current-thread loopback runtime counts allocation/reallocation calls and requested bytes across both endpoints after 16 warmup batches, over 64 measured batches; setup and teardown are excluded. Payloads are verified byte for byte. These exact counters are a different scope from the relay estimates.Header sweep: the permanent io_uring
web_headersbenchmark,cargo bench --locked --profile profiling -p moq-uring --bench web_headers -- --quick, completed all combinations. Indicative end-to-end time per batch (one uni and one bi per ready stream):This includes network scheduling and credit updates, not only parser CPU. The repeated integration fixture checks every completed fragmented tail and FIN; the implementation polls only new/woken header futures. Both new timing series run in the existing nightly targets.
Checks
Iteration after the merge queue rejection, at
24e2220bae10a5bac602944d8c0f012e8c67fda9:moq-ffi origin::supervision_tests::a_panicked_origin_task_reaches_the_host_while_another_origin_keeps_working, which doesn't touch the QUIC transports. Main already tracks it, from this same run, as the flakequest/m0/test-flakes-2/ffi-origin-panic.md.origin/maintwice (no rebase). The first merge brought in datagram tracks (moq-dev/moq#7752), MTU probes on validated paths (moq-dev/moq#7788), andObservationfields (moq-dev/moq#7776); the only textual conflicts wereCargo.lockandrs/moq-tokio/MIGRATE.md, and both migration entries are kept. The second brought in the ACME removal (moq-dev/moq#7803): moq-relay now drops bothx509-parser(main) andweb-transport-trait(this PR).a_read_reset_stays_reset_for_the_close_watch).unread_lease_holds_the_end_off, plusan_unread_lease_holds_the_close_watch_offin tokio and io_uring).a_stray_datagram_is_droppedin tokio and io_uring).just rs lint,cargo clippy --workspace --all-targets --all-features -D warnings, andcargo nextest run --all-features -p moq-quic -p moq-tokio -p moq-uring -p moq-relay -p moq-native -p moq-net -p qmux@0.7.0 -p moq-cliall pass: 5,688 passed, 82 skipped.just test interop --tail: all checks passed, on the first merge head.Earlier rounds:
5d47c8556c82a61f707ef3b84cac8ad122804c23,nix develop --command quest gate just ci: Rust lint/rustdoc passed; all 11,622 selected Rust tests passed (132 configured skips). Repository formatting, markdown, shell, TOML/Nix, bench checks, drift checks, quest validation and GitHub static checks passed.unshare: write failed /proc/self/uid_map: Operation not permitted; Bun'smoq-docscript cannot findtsc; the direnv hook regression test loses its mock PATH.just gate flake, the samejust js check-changed <doc-list>selection, andbash .claude/hooks/direnv.test.shreproduce them in the unchangedd1bcd5748bcheckout. The latter two use the same Codex snapshot as this checkout. No harness or environment setting was disabled to get a pass.quest check --base origin/main: 563 documents OK after quest deletion, dependent prose repairs, and sizing the childless API audit's remaining work. Final markdown check andgit diff --checkpass.pending=64, ready=1. The shared engine's batch threshold withheld all returned stream IDs. The engine test and repeated 16-batch integration sweep pass after the source fix. The permanent header benchmark now completes every combination.just check origin/mainwas also attempted: its ordinary flake probe failed while importing an untrackedcache-agent.sockbefore reaching the user-namespace probe. The strict flake module above reproduces the actual runner-image failure on the unchanged base. Recommend making the ordinary probe use a tracked Git source in the CI follow-up.git merge-tree --write-tree origin/main HEAD: clean.Review
Claude Code (
quest review --harness claude), Claude Opus 5.5, medium effort, sessionb3360aa6-0083-4c6f-a926-993d45ed50b3. Most of the code carries GPT-6.1 Sol trailers, so Claude reviews.32e6224677bbd621bfbb24010a5f75a9feef3564found two issues:reset_mutshapes differ because tokio's driver runs on another thread behind a lock, while io_uring's is a single-threadedRefCell; io_uring now has a comment saying so. The tokioexpectis the pre-PR invariant (basers/moq-tokio/src/quic/web/send.rs:108). The concrete drift it found, io_uring'spoll_resetdoc, is fixed. This repeats the extraction the earlier session refuted.poll_open_*(&self, waiter, options)has no per-call state, and the pre-PR adapter parked the same in-flight open in a session-wideOpSlot(basers/moq-tokio/src/transport/owned.rs:91-112). Each kind holds at most one, and the next open of that kind adopts it with its own options.01cf8b10faf44830d81f388715d96b1cbc744d57: 0 findings.b5c37c2d2abe118ea0e99934ce99bd5e639ec29aadds only merges oforigin/main. They resolveCargo.lockandrs/moq-relay/Cargo.toml(the ACME removal), thenquest/m2/net-wrap/README.md: main's runtime-seam link is kept, and this PR's transport-seam line drops the deletednative-web.mdlink.git diff origin/main...HEADadds and removes exactly the lines that round 2's head did against its base; only diff context differs. That is the guide's hand-resolved-merge exemption. Onb5c37c2d2a,just rs lintand workspace clippy pass, and 5,699 tests pass across the QUIC and dependent crates (82 skipped).The earlier session (
2085d17a, rounds 1-2 at5d47c855) refuted the wrapper extraction and accepted the rejection-status guards.Follow-ups
quest/m1/codex-env-snapshot.mdin moq-dev/moq#7747. It also replaces the mocked PATH in the direnv hook test. Recommend repairing snapshot composition in that work, without changing this transport PR's check harness.(Written by Claude Opus 5.5)