refactor(tick): align API with Rust guidelines - #632
Conversation
Make fallible conversions explicit, replace mutable clock configuration with a builder, strengthen error and timer contracts, and migrate workspace consumers to the revised API. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d077867b-9f23-43df-86db-c3efd05948f4
There was a problem hiding this comment.
Pull request overview
This PR refactors the tick crate’s public API to better align with Rust/Microsoft API guidelines, making time conversion failures explicit, introducing a ClockControlBuilder, improving timeout/error classification, and adjusting timer wake behavior so wakers can be invoked outside internal locks. It also updates downstream crates/tests/examples to use the revised APIs.
Changes:
- Replace infallible/panicking
system_time_asconversions with fallibletry_system_time_asonClock/SimpleClock, and migrate usages. - Rework timer advancement/waker handling to collect ready wakers and wake outside locks; add “latest waker wins” behavior for
Delay/PeriodicTimer. - Introduce
ClockControlBuilder(plusClockControl::new_auto_advancing) and extendtick::Errorwith timeout classification and backtrace capture.
Reviewed changes
Copilot reviewed 52 out of 52 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| crates/tick/src/timers.rs | Adds waker update support and changes timer advancement to collect ready wakers for out-of-lock wakeups. |
| crates/tick/src/timeout.rs | Simplifies Timeout to use Delay directly and returns a typed timeout error. |
| crates/tick/src/stopwatch.rs | Makes Stopwatch clonable and replaces From conversions with an explicit start() accessor. |
| crates/tick/src/state.rs | Wakes timer wakers after releasing synchronization guards. |
| crates/tick/src/simple_clock.rs | Adds docsrs feature annotations and introduces fallible try_system_time_as. |
| crates/tick/src/runtime/mod.rs | Inlines runtime re-exports in docs. |
| crates/tick/src/runtime/inactive_clock.rs | Strengthens marker traits/docs and adds docsrs feature annotation for rt-shared. |
| crates/tick/src/runtime/clock_gone.rs | Makes ClockGone trivially copyable/comparable for easier consumption. |
| crates/tick/src/periodic_timer.rs | Updates waker when polled by a different task; adds tests to ensure latest waker is notified. |
| crates/tick/src/lib.rs | Adjusts crate docs for feature-gated examples and inlines key public re-exports. |
| crates/tick/src/future_ext.rs | Updates timeout API to return Timeout<Self> and improves docs/example. |
| crates/tick/src/fmt/unix_seconds.rs | Updates doctests to avoid unwrap_err and simplifies parsing error conversion. |
| crates/tick/src/fmt/rfc_2822.rs | Updates doctest to avoid unwrap_err. |
| crates/tick/src/fmt/mod.rs | Updates docs to point to fallible conversion API and inlines formatter re-exports. |
| crates/tick/src/error.rs | Extends Error with timeout kind, classification helpers, and optional backtrace capture. |
| crates/tick/src/delay.rs | Updates waker when polled by a different task; adds tests to ensure latest waker is notified. |
| crates/tick/src/clock.rs | Introduces try_system_time_as, adds docsrs feature annotations, and supports timer waker updates. |
| crates/tick/src/clock_control.rs | Introduces ClockControlBuilder, new_auto_advancing, and wakes wakers after releasing the state lock. |
| crates/tick/README.md | Regenerates README to reflect the new API usage patterns. |
| crates/tick/examples/interop_time.rs | Migrates example away from infallible conversion API. |
| crates/tick/examples/interop_jiff.rs | Migrates example to try_system_time_as. |
| crates/tick/examples/interop_chrono.rs | Migrates example away from infallible conversion API. |
| crates/tick/examples/data.rs | Makes cached timestamp updates fallible and propagates errors. |
| crates/tick/examples/clock.rs | Makes example operation fallible to propagate conversion failures. |
| crates/tick/examples/clock_control.rs | Migrates to new_auto_advancing and avoids unwrap in example. |
| crates/tick/Cargo.toml | Updates crate metadata (category + documentation URL). |
| crates/seatbelt/tests/timeout.rs | Updates tests to use ClockControlBuilder/new_auto_advancing. |
| crates/seatbelt/tests/retry.rs | Updates tests to use new_auto_advancing. |
| crates/seatbelt/tests/hedging.rs | Updates tests to use new_auto_advancing. |
| crates/seatbelt/tests/chaos_latency.rs | Updates tests to use new_auto_advancing. |
| crates/seatbelt/src/timeout/service.rs | Updates internal tests to use ClockControlBuilder. |
| crates/seatbelt/src/retry/service.rs | Updates internal tests to use new_auto_advancing. |
| crates/seatbelt/src/hedging/service.rs | Updates internal tests to use new_auto_advancing. |
| crates/seatbelt/src/chaos/latency/service.rs | Updates internal tests to use ClockControlBuilder. |
| crates/seatbelt_http/src/timeout.rs | Updates tests to use new_auto_advancing. |
| crates/seatbelt_http/src/retry.rs | Updates tests to use new_auto_advancing. |
| crates/seatbelt_http/src/hedging.rs | Updates tests to use new_auto_advancing. |
| crates/seatbelt_http/src/breaker.rs | Updates tests to use new_auto_advancing. |
| crates/http_extensions/src/fake_handler.rs | Updates test clock creation to use new_auto_advancing. |
| crates/http_extensions/src/body/timeout_body.rs | Updates tests to use new_auto_advancing. |
| crates/fetch/tests/timeout.rs | Updates tests to use new_auto_advancing. |
| crates/fetch/tests/standard_pipeline.rs | Updates tests/helpers to use new_auto_advancing. |
| crates/fetch/tests/resilience.rs | Updates tests/helpers to use new_auto_advancing. |
| crates/fetch/src/fake.rs | Updates fake builder test to use ClockControlBuilder. |
| crates/fetch_hyper/tests/smoke.rs | Updates helper clock to use new_auto_advancing. |
| crates/fetch_hyper/src/tls/connector.rs | Updates tests to use new_auto_advancing. |
| crates/fetch_hyper/src/timer.rs | Updates tests to use new_auto_advancing. |
| crates/fetch_hyper/src/testing.rs | Updates tests to use new_auto_advancing. |
| crates/fetch_hyper/src/connection/hyper_handler.rs | Updates tests to use new_auto_advancing. |
| crates/fetch_hyper/src/connection/hyper_connector_adapter.rs | Updates tests to use new_auto_advancing. |
| crates/fetch_hyper/src/connection/client_connector.rs | Updates tests to use new_auto_advancing. |
| crates/fetch_hyper/src/builder.rs | Updates docs/tests to use new_auto_advancing. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #632 +/- ##
========================================
Coverage 100.0% 100.0%
========================================
Files 472 473 +1
Lines 45447 45553 +106
========================================
+ Hits 45447 45553 +106
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Add coverage for error classification, backtrace storage, builder defaults, and maximum-instant timer advancement. Also use the preferred duration unit and guard auto-advance progress so mutation testing cannot hang. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d077867b-9f23-43df-86db-c3efd05948f4
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 52 out of 52 changed files in this pull request and generated no new comments.
Suppressed comments (1)
crates/tick/src/runtime/inactive_clock.rs:44
- The
Sharedmode bullet says it “Does not implementClone”, butSharednow derivesClone/Copy. Even if the intent is to describeInactiveClock<Shared>, the current wording is ambiguous and can be read as referring to the marker type itself. Clarify that the inactive clock specialization is the non-Clone/non-ThreadAwaretype.
Remove redundant derives, rustdoc attributes, and the unused stopwatch start accessor while preserving the correctness-related timer changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d077867b-9f23-43df-86db-c3efd05948f4
Remove redundant rustdoc attributes and retain expired wakers in their existing map nodes until locks are released. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d077867b-9f23-43df-86db-c3efd05948f4
Keep builder defaults solely in ClockControl::builder rather than exposing an unnecessary Default implementation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d077867b-9f23-43df-86db-c3efd05948f4
|
Restore marker trait implementations to their pre-PR surface and avoid unnecessary public trait exposure. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d077867b-9f23-43df-86db-c3efd05948f4
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 50 out of 50 changed files in this pull request and generated no new comments.
Suppressed comments (1)
crates/tick/src/error.rs:187
Errorcaptures and stores aBacktrace, but thestd::error::Errorimpl does not expose it via the trait. As a result, whentick::Erroris used behinddyn std::error::Error(or wrapped by other error types), callers will not be able to retrieve the backtrace through the standardError::backtrace()API.
Consider overriding fn backtrace(&self) in the std::error::Error impl to return the captured backtrace.
## Problem Pull requests from forks can never merge. They sit forever on: > Code scanning is waiting for results from CodeQL for the commits `<sha>` or `<sha>`. Current example: #624 (from `sandersaares/oxidizer`). **Root cause:** this repo uses CodeQL **default setup**, and default setup does not run for pull requests from forks — [that exclusion is documented behaviour](https://docs.github.com/en/code-security/concepts/code-scanning/setup-types), not a bug. The code-scanning merge-protection rule still expects a `CodeQL` result, so the check stays pending indefinitely and the PR is permanently blocked. Evidence gathered on this repo: | PR | Head repo | Fork? | CodeQL run | CodeQL check | |----|-----------|-------|-----------|--------------| | #621, #632, #568, #570-#573 | `microsoft/oxidizer` | no | yes | yes | | #624 | `sandersaares/oxidizer` | yes | none | none, blocked | | #622 | `kate-shine/oxidizer` | yes | none | none, blocked | ## Why advanced setup fixes it An advanced-setup workflow triggered by `pull_request` **does** run for fork PRs, and GitHub accepts its SARIF upload on public repositories even though the fork's `GITHUB_TOKEN` is read-only. Verified empirically against `prettier/prettier`, a public repo on advanced setup. On fork PR head `bf2849cee9d467aedf3fbd42a95201ea4393f855` (from `splincode/prettier`): - the only workflow runs are `pull_request` ones, including `.github/workflows/codeql.yml`; there is no `dynamic` (default setup) run at all; - yet `github-advanced-security` posted check `CodeQL` with conclusion `success`. ## History `.github/workflows/codeql.yml` already exists and is correct, but it has been in the `disabled_manually` state since **2026-01-22** and has not run since. It was introduced by #219 ("ci: Switch to advanced CodeQL mode"), whose stated goal was, verbatim, *"Once this is checked in, then we can tweak to improve permissions on forks"* — exactly the problem above. It ran 33 times, all successful, and roughly 4.5 hours after that PR merged, default setup was switched back on in the repository settings, which automatically disabled this workflow. No PR, issue or comment records a reason. Since then #232, #237, #462, #470, #484, #543, #563 and #574 have all been maintaining a workflow that never runs. ## Changes - Document why advanced setup is preferred, so this does not get silently reverted to default setup a second time. - Add the `merge_group` trigger. `main.yml` and `anvil-pr.yml` both have it and `codeql.yml` did not; without it the merge queue stalls on this workflow once it is required. - Bump `github/codeql-action` to `v4.37.3` (confirmed `"immutable": true` via the releases API, consistent with the existing tag-pinning rationale in the file). ## Required admin action, this PR alone is not sufficient Merging this changes nothing by itself, because the workflow is still disabled and GitHub will keep it disabled while default setup is on. A repository admin must, **in this order**: 1. Settings, Advanced Security, **disable CodeQL default setup**. 2. Re-enable this workflow: `gh api -X PUT repos/microsoft/oxidizer/actions/workflows/codeql.yml/enable`. Doing step 2 first does not work. ## Trade-off Fork PRs from **first-time contributors** will show CodeQL as `action_required` until a maintainer clicks "Approve and run". That is standard GitHub Actions fork policy and it also applies to every other `pull_request` workflow in this repo today. It is a single click, versus the current situation where fork PRs cannot merge at all. ## Draft Left as a draft until an admin confirms they will make the settings change, since merging without it is a no-op. Co-authored-by: Kateřina Churanová <katerina.churanova@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Overview
Aligns the
tickpublic API with the Rust API Guidelines and Microsoft Rust Guidelines. The change makes time conversion failures explicit, replaces mutable test-clock configuration with a builder, strengthens error and future contracts, and fixes timer waker handling so callbacks run outside internal locks.Workspace consumers are migrated to the revised API to keep behavior consistent. This is justified by clearer failure semantics, a smaller and more predictable public surface, feature-safe documentation, and correct async task wake behavior.
Validation
tickfeature combination compilesfetch,fetch_hyper,http_extensions,seatbelt, andseatbelt_httpcompile with all features and targets