Skip to content

feat(kio): add a poll-native Deadline and adopt it in moq-net - #2536

Merged
kixelated merged 2 commits into
mainfrom
claude/kio-wait-polling-refactor-49f5fd
Jul 27, 2026
Merged

feat(kio): add a poll-native Deadline and adopt it in moq-net#2536
kixelated merged 2 commits into
mainfrom
claude/kio-wait-polling-refactor-49f5fd

Conversation

@kixelated

Copy link
Copy Markdown
Collaborator

Summary

Six sites in moq-net hand-rolled a wall-clock deadline inside a kio::wait closure. Each pinned an async block whose None arm was std::future::pending(), then fused it with a separate fired flag and an is_some() guard:

let mut sleep = std::pin::pin!(async {
    match deadline {
        Some(at) => web_async::time::sleep(at.saturating_duration_since(web_async::time::Instant::now())).await,
        None => std::future::pending().await,
    }
});
let mut fired = false;
kio::wait(|waiter| {
    ...
    if deadline.is_some() && !fired && waiter.poll_future(sleep.as_mut()).is_ready() { fired = true; }
    match fired { true => Poll::Ready(Step::Expired), false => Poll::Pending }
})

kio::time::Deadline collapses that to deadline.set(at) plus deadline.poll(waiter).

  • Both clock backends behind web_async::time (tokio natively, wasmtimer in the browser) already expose Sleep::is_elapsed and Sleep::reset, so the fuse comes for free and re-arming reuses the allocation instead of boxing a fresh future every loop turn.
  • Deadline::set is a no-op for the instant it already holds, which is what lets the linger loops in origin.rs recompute and re-set their deadline every turn without restarting the countdown. The (true, at) => at arm depends on this.
  • Constructing the inner Sleep is deferred to the first poll: on native it panics without a live tokio time driver, and only the poll is guaranteed to run inside the executor. So new/set stay callable anywhere, and the runtime requirement lands exactly where it does today.
  • The probe interval in lite/publisher.rs no longer pins interval.tick(). Interval::poll_tick is already poll-native on both backends and needs no wrapper.

kio::tokio::Sleep is superseded: native-only, no re-arm, no disarm, no fuse. It had no callers anywhere, and nothing in the workspace enabled the tokio feature. Hidden and deprecated here; the removal (plus dropping the optional tokio dependency) is a breaking change to a published crate and belongs on dev as a follow-up.

Net -34 lines across the converted sites.

Public API changes

All additive, so this targets main.

  • Added kio::time module behind a new time cargo feature (dep:web-async).
  • Added kio::time::Deadline with new, at, after, set, deadline, poll, wait, plus Default and Debug.
  • Added kio::time::{Instant, Duration} re-exports from web_async::time.
  • Deprecated kio::tokio::Sleep (#[deprecated]) and hid kio::tokio (#[doc(hidden)]). Not a removal, so not breaking. Verified the warning reaches a caller rather than being swallowed by the #[allow(deprecated)] on the impl block.
  • moq-net gains no public surface; all six converted sites are private.

Test plan

  • 7 new Deadline tests under #[tokio::test(start_paused = true)], covering firing, disarmed-never-fires, fused-once-elapsed, idempotent re-arm, deferred re-arm through Sleep::reset, disarm mid-countdown, and re-arm after disarm.
  • just fix clean, just rs check clean (clippy -D warnings, cargo fmt --check, rustdoc -D warnings, cargo shear, cargo sort).
  • cargo test -p kio --all-features: 26 pass. cargo test -p moq-net: 557 pass. Full just rs test: 1941 pass.
  • cargo check -p kio --features time --target wasm32-unknown-unknown builds, pulling wasmtimer as expected. The moq-net wasm build fails on a pre-existing getrandom backend error, identical with these changes stashed.

Unrelated failure seen while testing

moq-srt's accepted_socket_sends_a_burst_larger_than_srt_tokio_default failed once in the full run. It is pre-existing and not caused by this change: on a stashed tree it fails 2 of 8 consecutive runs, versus 1 of 5 here. It sends 1024 messages over a real SRT/UDP loopback socket and asserts delivery inside a fixed 3-second wall-clock budget on real timers, so it is timing-sensitive under load. Tracked separately rather than papered over with a longer timeout.

Cross-Package Sync

No rows apply. No wire format, moq-ffi, CLI, or relay config surface changed, and moq-net's public API is untouched.

🤖 Generated with Claude Code

(Written by Opus 5)

Six sites in moq-net hand-rolled a wall-clock deadline inside a `kio::wait`
closure, each pinning an async block whose `None` arm was `std::future::pending()`,
then fusing it with a separate `fired` flag and an `is_some()` guard. Three
moving parts to express "an optional deadline that fires once".

`kio::time::Deadline` replaces all of it. Both clock backends behind
`web_async::time` (tokio natively, wasmtimer in the browser) already expose
`Sleep::is_elapsed` and `Sleep::reset`, so the fuse comes for free and re-arming
reuses the allocation instead of boxing a fresh future every loop turn.
Construction of the inner `Sleep` is deferred to the first poll, since on native
it panics without a live tokio time driver, and only the poll is guaranteed to
run inside the executor.

The probe interval in lite/publisher no longer pins `interval.tick()` either;
`Interval::poll_tick` is already poll-native on both backends.

`kio::tokio::Sleep` is superseded: it is native-only, cannot be re-armed or
disarmed, and has no fuse. It had no callers in or out of the module, and
nothing in the workspace enabled the `tokio` feature. Hidden and deprecated
here; removal is a breaking change and belongs on dev.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@sourcery-ai sourcery-ai 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.

Sorry @kixelated, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@kixelated, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 12 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ac47a712-8de5-4855-8e3b-9767b63461d6

📥 Commits

Reviewing files that changed from the base of the PR and between b2f8918 and 5cdbaf0.

📒 Files selected for processing (2)
  • rs/kio/src/time.rs
  • rs/kio/src/tokio.rs

Walkthrough

Adds a feature-gated kio::time::Deadline backed by web-async, with re-arming, disarming, fused polling, async waiting, and tests. The existing Tokio Sleep type is deprecated. moq-net enables the new feature and migrates request timeouts, alias resolution, probe intervals, route announcement linger handling, front expiry, and track idle linger handling to deadline or direct interval polling.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding a poll-native Deadline in kio and using it in moq-net.
Description check ✅ Passed The description is directly related to the changeset and accurately explains the new Deadline API and its adoption.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/kio-wait-polling-refactor-49f5fd

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rs/kio/src/time.rs`:
- Line 13: Add a doc comment immediately above the public re-export of Duration
and Instant explaining that these types come from web_async::time and
documenting the dependency’s major-version coupling. Keep the existing re-export
unchanged.
- Around line 53-56: Update Time’s after method to use checked Instant
arithmetic instead of Instant::now() + duration. Mirror the existing origin.rs
pattern with an inlined checked_add result, preserving the deadline behavior
while ensuring non-representable durations never panic.

In `@rs/kio/src/tokio.rs`:
- Around line 16-19: Add the #[doc(hidden)] attribute to the deprecated public
Sleep struct, alongside its existing #[deprecated(note = "...")] attribute, so
the item is excluded from generated documentation while preserving the
deprecation notice.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c4b97578-3c6d-4edd-b521-b8ecbad9d810

📥 Commits

Reviewing files that changed from the base of the PR and between 1c2584f and b2f8918.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • rs/kio/Cargo.toml
  • rs/kio/src/lib.rs
  • rs/kio/src/time.rs
  • rs/kio/src/tokio.rs
  • rs/moq-net/Cargo.toml
  • rs/moq-net/src/ietf/control.rs
  • rs/moq-net/src/ietf/subscriber.rs
  • rs/moq-net/src/lite/publisher.rs
  • rs/moq-net/src/model/origin.rs

Comment thread rs/kio/src/time.rs
Comment thread rs/kio/src/time.rs
Comment thread rs/kio/src/tokio.rs
- Document that `Duration`/`Instant` are re-exported from `web-async`, so a
  major bump there is a breaking change for them.
- `Deadline::after` now uses checked arithmetic. A duration the clock cannot
  represent (e.g. `Duration::MAX`) leaves the deadline disarmed instead of
  panicking on the overflow, matching how `run_front` already treats an
  unrepresentable linger.
- Add the item-level `#[doc(hidden)]` to the deprecated `kio::tokio::Sleep`
  alongside its `#[deprecated]`, per the repo's deprecation convention.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@kixelated
kixelated merged commit 028cfc6 into main Jul 27, 2026
3 checks passed
@kixelated
kixelated deleted the claude/kio-wait-polling-refactor-49f5fd branch July 27, 2026 21:00
kixelated added a commit that referenced this pull request Jul 27, 2026
…cate-announcements-34ef49

`main` replaced the pinned-sleep-plus-`fired` timer scaffolding with
`kio::time::Deadline` (#2536) in the two loops this branch also edits. Took
main's shape in both and kept this branch's additions on top: the per-track
`skip` closure in `serve_track`, and the route-table wording in the announce
loop.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Jul 27, 2026
ksletmoe-aws added a commit to ksletmoe-aws/moq that referenced this pull request Jul 27, 2026
* 'main' of https://github.com/moq-dev/moq: (43 commits)
  feat(cli): add jemalloc heap profiling (moq-dev#2539)
  feat(kio): add a poll-native Deadline and adopt it in moq-net (moq-dev#2536)
  test(moq-srt): size the burst test below the SRT sender's drop window (moq-dev#2540)
  refactor(net): replace the global LRU cache pool with per-track write-time eviction (moq-dev#2526)
  ci: compile-check the Windows and macOS code paths on PRs (moq-dev#2531)
  chore(net): require @moq/qmux 0.3.2 for fallback reset codes (moq-dev#2535)
  fix(hang,watch): deliver non-sequential group ids incrementally (moq-dev#2484)
  feat(moq-audio): play decoded PCM out a speaker (moq-dev#2529)
  fix(js/publish): capture video through a worker MediaStreamTrackProcessor (moq-dev#2528)
  feat(net): surface a peer's stream reset code as a remote error (moq-dev#2510)
  feat(mux): expose catalog bitrate/jitter measurement as catalog::Estimator (moq-dev#2530)
  fix(net): release cache-pool registrations so publishers stop leaking (moq-dev#2525)
  build(deps): bump the github-actions group with 2 updates (moq-dev#2524)
  build(deps-dev): bump ruff from 0.15.21 to 0.15.22 in the uv group (moq-dev#2523)
  build(deps): bump the bun group with 2 updates (moq-dev#2522)
  chore: bump non-Rust package patch versions (moq-dev#2519)
  fix(hang): reject a non-hex catalog description instead of misreading it (moq-dev#2516)
  build(deps): bump the github-actions group across 1 directory with 6 updates (moq-dev#2464)
  refactor(net): poll kio readiness directly (moq-dev#2515)
  chore: release (moq-dev#2491)
  ...

# Conflicts:
#	rs/moq-net/src/client.rs
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