Skip to content

feat(tick): add TimeClock for driver-free time retrieval#537

Open
martintmk wants to merge 13 commits into
mainfrom
feat/tick-thread-driven-clock
Open

feat(tick): add TimeClock for driver-free time retrieval#537
martintmk wants to merge 13 commits into
mainfrom
feat/tick-thread-driven-clock

Conversation

@martintmk

@martintmk martintmk commented Jun 30, 2026

Copy link
Copy Markdown
Member

Summary

Adds TimeClock, a simplified clock used purely for time retrieval (no timers). Unlike Clock, it carries no timers, so it needs no async runtime and no driverTimeClock::new_system() returns a ready-to-use clock backed by real OS time.

TimeClock is the common abstraction shared by every clock kind, so time-only APIs (e.g. Stopwatch) accept either a full Clock or a TimeClock seamlessly.

Note: This supersedes the earlier discouraged thread-driven clock constructor, which is reverted in this PR.

API

  • TimeClock (new public type):
    • TimeClock::new_system() — driver-free clock backed by real OS time.
    • TimeClock::new_frozen() / new_frozen_at(time) — frozen clocks (mirrors Clock, gated behind test-util).
    • system_time(), instant(), system_time_as::<T>(), stopwatch().
    • Implements AsRef<TimeClock> and ThreadAware.
  • Clock (unchanged public surface):
    • Now implements AsRef<TimeClock>.
    • New Clock::time_clock() -> &TimeClock accessor.
    • Time-read methods delegate to the internal TimeClock (single source of truth).
  • ClockControl:
    • New ClockControl::to_time_clock() — controlled TimeClock driven by the same ClockControl as to_clock(); both observe identical controlled time.
  • Stopwatch:
    • Stopwatch::new(impl AsRef<TimeClock>) — accepts &Clock, &TimeClock, or an owned TimeClock. Existing clock.stopwatch() / Stopwatch::new(&clock) call sites compile unchanged via std's blanket &T: AsRef<U>.
    • Internally simplified: the old #[cfg] split is gone; production cost remains a single Instant (TimeClock::System is zero-sized).

Seamlessness

use tick::{Stopwatch, TimeClock};

let clock = TimeClock::new_system();      // no runtime / driver needed
let _now = clock.system_time();

let sw = Stopwatch::new(&clock);          // also accepts a `Clock`
let _elapsed = sw.elapsed();

Docs

  • New crate-level "Time retrieval without timers" section in lib.rs + Overview bullet.
  • README.md regenerated via just readme.

Testing

  • cargo test -p tick --all-features: 143 unit + 54 doctests pass.
  • cargo clippy -p tick --all-features --all-targets -- -D warnings: clean.
  • cargo doc -p tick --all-features: clean.
  • No-default-features build OK; just spellcheck passes; formatted with just format.

Add Clock::new_thread_driven behind the new thread-driven feature. It spawns a dedicated background OS thread that periodically advances the clock's timers, requiring no async runtime or manual ClockDriver polling. The thread terminates once all Clock clones are dropped.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.0%. Comparing base (75cac4d) to head (9846574).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #537   +/-   ##
=======================================
  Coverage   100.0%   100.0%           
=======================================
  Files         353      354    +1     
  Lines       26821    26887   +66     
=======================================
+ Hits        26821    26887   +66     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Introduce TimeClock, a simplified clock exposing time retrieval only (no timers), requiring no runtime or driver via TimeClock::new_system/new_frozen/new_frozen_at. Clock implements AsRef<TimeClock> and exposes time_clock(); ClockControl gains to_time_clock(); Stopwatch::new now accepts impl AsRef<TimeClock> so both clock kinds work seamlessly.

This supersedes and reverts the discouraged thread-driven clock constructor.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@martintmk martintmk changed the title feat(tick): add thread-driven self-driving clock constructor feat(tick): add TimeClock for driver-free time retrieval Jun 30, 2026
Add From<ClockControl>/From<&ClockControl> for TimeClock (parity with Clock) and From<Clock>/From<&Clock> for TimeClock for ergonomic downgrade.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@martintmk martintmk marked this pull request as ready for review June 30, 2026 15:54
Copilot AI review requested due to automatic review settings June 30, 2026 15:54

Copilot AI 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.

Pull request overview

This PR introduces TimeClock, a new public, driver-free clock abstraction in tick for time retrieval only (no timers), and refactors existing time-read APIs to share this single source of truth. It updates Stopwatch to accept any AsRef<TimeClock> so callers can use either a full timer-capable Clock or a lightweight TimeClock interchangeably.

Changes:

  • Add TimeClock (new_system, controlled/frozen constructors under test-util) and wire it into Clock as the delegated implementation for system_time, instant, and system_time_as.
  • Update Stopwatch to store a TimeClock + start Instant, and broaden Stopwatch::new(...) to accept impl AsRef<TimeClock>.
  • Update crate-level docs and regenerated README to document “time retrieval without timers”.

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
crates/tick/src/time_clock.rs New TimeClock type providing driver-free time retrieval and controlled/frozen variants for tests.
crates/tick/src/stopwatch.rs Refactors Stopwatch to accept any AsRef<TimeClock> and simplifies internal representation.
crates/tick/src/lib.rs Exposes TimeClock and documents the new “time retrieval without timers” concept.
crates/tick/src/clock.rs Adds Clock::time_clock() and delegates time-read methods to the embedded TimeClock.
crates/tick/src/clock_control.rs Adds ClockControl::to_time_clock() and From impls to create controlled TimeClocks.
crates/tick/README.md Regenerated documentation reflecting the new TimeClock API and narrative.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/tick/src/time_clock.rs Outdated
Comment on lines +155 to +162
pub fn system_time_as<T: TryFrom<SystemTime>>(&self) -> T {
match T::try_from(self.system_time()) {
Ok(time) => time,
Err(_err) => {
panic!("The SystemTime returned by the clock is always in normalized range and must be convertible to the target type.",)
}
}
}
Comment on lines +193 to +195
pub fn to_time_clock(&self) -> crate::TimeClock {
crate::TimeClock::from_state(&ClockState::ClockControl(self.clone()))
}
Copilot AI review requested due to automatic review settings June 30, 2026 15:59

Copilot AI 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.

Pull request overview

Copilot reviewed 6 out of 7 changed files in this pull request and generated 2 comments.

Comment thread crates/tick/src/time_clock.rs Outdated
Comment on lines +155 to +160
pub fn system_time_as<T: TryFrom<SystemTime>>(&self) -> T {
match T::try_from(self.system_time()) {
Ok(time) => time,
Err(_err) => {
panic!("The SystemTime returned by the clock is always in normalized range and must be convertible to the target type.",)
}
Comment thread crates/tick/src/time_clock.rs Outdated
Copilot AI review requested due to automatic review settings June 30, 2026 16:04
martintmk and others added 2 commits June 30, 2026 18:05
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Add TimeClock::from_control taking an owned ClockControl and use it from ClockControl::to_time_clock and From<ClockControl>, eliminating the extra Arc clone that went through a temporary ClockState. Also adds a Stopwatch regression test covering all AsRef<TimeClock> inputs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI 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.

Pull request overview

Copilot reviewed 6 out of 7 changed files in this pull request and generated 2 comments.

Comment thread crates/tick/src/time_clock.rs Outdated
Comment on lines +156 to +161
match T::try_from(self.system_time()) {
Ok(time) => time,
Err(_err) => {
panic!("The SystemTime returned by the clock is always in normalized range and must be convertible to the target type.")
}
}
Copilot AI review requested due to automatic review settings June 30, 2026 16:09

Copilot AI 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.

Pull request overview

Copilot reviewed 6 out of 7 changed files in this pull request and generated 3 comments.

Comment thread crates/tick/src/time_clock.rs
Comment thread crates/tick/src/time_clock.rs Outdated
Comment thread crates/tick/src/clock.rs Outdated
Comment on lines 363 to 365
/// `test-util` feature), where time could be moved excessively far into the future,
/// potentially exceeding the target type's supported range of values. This is not a concern
/// in production.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 30, 2026 16:15
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI 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.

Pull request overview

Copilot reviewed 6 out of 7 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings June 30, 2026 16:20
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI 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.

Pull request overview

Copilot reviewed 6 out of 7 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings June 30, 2026 16:26

Copilot AI 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.

Pull request overview

Copilot reviewed 6 out of 7 changed files in this pull request and generated no new comments.

Comment thread crates/tick/src/clock.rs
#[derive(Clone)]
pub struct Clock {
state: ClockState,
time: TimeClock,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

TimeClock does not really English for me. Suggested name: TimeSource or just Time

Comment thread crates/tick/src/clock.rs
/// - The system time returned is always within a normalized range in real environments.
/// - Target types that implement [`TryFrom<SystemTime>`][TryFrom] typically support the full valid
/// range of system time values.
/// For types that support the full valid range of system time values, this conversion always

@sandersaares sandersaares Jul 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The phrase "full valid range of system time values" is confusing. Is "system time" the same as "SystemTime" or something different? What does the word "valid" mean - is there an "invalid range" of system time values that should also be accounted for? How do I know if a SystemTime instance is valid or invalid, if so?

use crate::state::ClockState;
use crate::thread_aware_move;

/// A simplified clock used purely for **time retrieval**.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I suggest re-phrasing this type purely in terms of what it is for, instead of it being a "variant" of another thing. The variant-based description creates needless relationships the reader needs to resolve in their head.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Well, the variant is really what this thing is. I don't want to introduce new word or concept. We have a "simple clock" that does just time retrieval and then clock that does time retrieval + timers handling. We just need to find a good name for it.

  • PlainClock
  • SimpleClock
  • BasicClock

(I am not happy about neither of these either)

Comment thread crates/tick/src/time_clock.rs
Comment thread crates/tick/src/time_clock.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.

3 participants