Skip to content

feat(net)!: inject an explicit Runtime for session timers and machines - #3007

Open
kixelated wants to merge 2 commits into
devfrom
moq-uring-design
Open

feat(net)!: inject an explicit Runtime for session timers and machines#3007
kixelated wants to merge 2 commits into
devfrom
moq-uring-design

Conversation

@kixelated

Copy link
Copy Markdown
Collaborator

Summary

  • Add moq_net::runtime: an explicitly injected Runtime trait (associated Transport and Timer types, timer(), a defaulted now(), and spawn) that supplies the two things a session cannot do alone: run its protocol runtime::Machine and wake its timers. No thread-locals, no globals, no ambient executor, and no Send bounds anywhere in the trait: pinning one transport type per runtime lets each implementation know the concrete machine it spawns, so a work-stealing tokio runtime and a future pinned !Send io_uring runtime are both expressible with no feature flags.
  • Client::connect(runtime, transport) / Server::accept(runtime, transport) now return the plain Session; the public Driver type is removed and the machine goes straight to Runtime::spawn.
  • Every session-side timer (bandwidth sampling, GOAWAY enforcement, control-stream and advertise timeouts, IETF subscription linger and retry, lite PROBE cadence, track-alias timeout) arms through the injected runtime instead of kio::time.
  • Runtime implementations: moq_tokio::runtime::Runtime<S> (tokio spawn + tokio sleeps; now() reads tokio's pausable clock) and runtime::Inline<S> (hands the machine back for callers that drive it in place; the relay's WebSocket handler keeps its inline drain semantics through it), browser runtimes in moq-wasm (public, for direct Rust-on-wasm users) and moq-ffi, and moq_net::runtime::Test behind the new test-runtime feature: a deterministic virtual-clock runtime with explicit advance / advance_to_timer / tick and no auto-advance.
  • moq-net's own tests keep their tokio::time::pause() semantics through a cfg(test) tokio-backed runtime, so the 67 paused tests migrate mechanically rather than semantically.

This is PR 1 of the plan on #2875 (#2875 (comment)): the moq-net prerequisite for the thread-per-core io_uring runtime.

Two deviations from that plan, discovered during implementation:

  1. Runtime keeps a now() (defaulted to the real clock). The plan said no clock on the trait, but relative arming (now + interval) and stamp-derived deadlines (linger) must read the virtual clock under a virtual-time test, or an advance() leaves every subsequently armed instant in the past (the bandwidth sampler would fire on every poll, forever). The default keeps real runtimes on the std clock; only Test overrides it.
  2. kio keeps kio::time for now. The model layer also owns deadlines (the origin driver's serve/linger machinery) and ~80 model tests manipulate the paused clock, so the kio 0.6 time/tokio removal moves to the follow-up PR that migrates the model and origin::Driver. This PR converts the session side, which is everything a session runtime needs.

Public API changes

  • moq-net (breaking, hence dev): Client::connect / Server::accept gain a leading runtime parameter and return Session instead of (Session, Driver); Driver is removed; Request gains a runtime parameter; new pub mod runtime (Runtime, Timer, Deadline, Machine, Instant, and feature-gated Test/Never); new test-runtime feature.
  • moq-tokio: new pub mod runtime (Runtime<S>, Inline<S>, Timer). Its own Client/Server surfaces are unchanged; consumers of moq-tokio need no changes.
  • moq-wasm: new pub mod runtime for direct Rust-on-wasm users of moq_net::Client.

Wire behavior changes

None. No draft, JS, or cross-language synchronization is required; doc/lib/rs/env/wasm.md (the one doc example using moq-net directly) and rs/CLAUDE.md are updated.

Test plan

  • nix develop --command just fix
  • nix develop --command just check
  • nix develop --command just test (3,478 passed, 2 skipped)
  • New unit tests for runtime::Test timer semantics (fire/re-arm/disarm/wake/advance-to-timer, and a regression for the re-arm-after-advance hazard that motivated now()).

(Written by Claude Fable 5)

🤖 Generated with Claude Code

https://claude.ai/code/session_01BCxzHiGxN5qi8Gd687nsgm

moq-net never spawns onto an ambient executor and never reads ambient time
again. Client::connect and Server::accept take a moq_net::Runtime (associated
Transport and Timer types, a spawn for the session's protocol machine, and the
clock its deadlines arm against) and return the plain Session; the public
Driver type is gone, replaced by runtime::Machine handed straight to the
runtime. Session-side timers (bandwidth sampling, GOAWAY enforcement, control
and advertise timeouts, subscription linger, PROBE cadence) all arm through
the injected runtime, so a thread-per-core io_uring runtime can drive sessions
without tokio.

The trait carries no Send bounds anywhere: pinning one transport type per
runtime lets each implementation know the concrete machine it spawns, so
moq-tokio keeps plain tokio::spawn (its transports are Send) while a pinned
!Send transport stays expressible. moq-tokio ships runtime::Runtime<S> plus
runtime::Inline (hands the machine back for callers that drive it in place,
which the relay's WebSocket handler does); moq-wasm and moq-ffi ship the
browser runtime; moq_net::runtime::Test (feature test-runtime) is a
deterministic virtual-clock runtime for tests.

Deviations from the design posted on #2875, discovered during implementation:
Runtime keeps a defaulted now() because relative arming (now + interval) and
stamp-derived deadlines must read the virtual clock in tests or an advance
leaves them in the past; and the model layer also owns deadlines (origin
serve/linger), so kio::time survives for model internals and its removal
moves to the follow-up PR that migrates the model and origin driver.

Part of #2875 (the runtime prerequisite for moq-uring).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BCxzHiGxN5qi8Gd687nsgm

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a5a8304969

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rs/moq-net/src/client.rs
Comment on lines +136 to +137
R: crate::runtime::Runtime + MaybeSend + MaybeSync + 'static,
R::Timer: MaybeSend,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove native Send bounds from the runtime path

On non-wasm targets, MaybeSend/MaybeSync impose Send/Sync, so these bounds, together with R::Timer: MaybeSend and the native MaybeSendBox = dyn Future + Send, reject the thread-pinned !Send runtimes and machines this new API is intended to enable. A Linux io_uring runtime using local state cannot call connect/accept or box its protocol future, despite Runtime itself advertising no Send requirement. Make sendness follow the chosen runtime/transport rather than the compilation target before publishing this breaking API shape.

AGENTS.md reference: AGENTS.md:L150-L152

Useful? React with 👍 / 👎.

…ds time (#3009)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: GPT-5 <noreply@openai.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d97da97cc7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

type Transport = S;

fn spawn(&self, machine: moq_net::runtime::Machine<Self>) {
*self.slot.lock().unwrap() = Some(machine);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject reuse of an occupied inline runtime

If cloned Inline handles are used to connect or accept a second session before take() is called, this assignment silently drops the first machine and replaces it. The first returned Session then remains open but has no protocol driver, so it never makes progress or processes shutdown. Make the runtime single-use by construction, or at minimum reject an occupied slot instead of overwriting it.

AGENTS.md reference: AGENTS.md:L158-L163

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant