maitake/time/
clock.rs

1//! [`Clock`]s provide a mechanism for tracking the current time.
2//!
3//! See the documentation for the [`Clock`] type for more details.
4use super::{
5    timer::{self, TimerError},
6    Duration,
7};
8use core::{
9    fmt,
10    ops::{Add, AddAssign, Sub, SubAssign},
11};
12
13/// A hardware clock definition.
14///
15/// A `Clock` consists of a function that returns the hardware clock's current
16/// timestamp in [`Ticks`] (`now()`), and a [`Duration`] that defines the amount
17/// of time represented by a single tick of the clock.
18///
19/// # Using `Clock`s
20///
21/// A `Clock` must be provided when [constructing a `Timer`](super::Timer::new).
22/// The `Clock` provided to [`Timer::new`](super::Timer::new) is used to
23/// determine the time to advance to when turning the timer wheel, and to
24/// determine the start time when adding a [`Sleep`](super::Sleep) future to the
25/// timer wheel.
26///
27/// In addition, once a global timer is set using the
28/// [`set_global_timer`](super::set_global_timer) function, the
29/// [`Instant::now()`] function may be used to produce [`Instant`]s representing
30/// the current timestamp according to that timer's [`Clock`]. The [`Instant`]
31/// type is analogous to the [`std::time::Instant`] type in the Rust standard
32/// library, and may be used to compare the time elapsed between two events, as
33/// a timestamp for logs and other diagnostics, and similar purposes.
34///
35/// # Implementing `now()`
36///
37/// Constructing a [new `Clock` definition](Self::new) takes a function, called
38/// `now()`, that returns the current hardware timestamp in a 64-bit number of,
39/// _ticks_. The period of time represented by a tick is indicated by the
40/// `tick_duration` argument to [`Clock::new`]. In order to define a `Clock`
41/// representing a particular hardware time source, a `now()` function must be
42/// implemented using that time source.
43///
44/// ## Monotonicity
45///
46/// Implementations of `now()` MUST ensure that timestamps returned by
47/// `now()` MUST be [monontonically non-decreasing][monotonic]. This means that
48/// a call to `now()` MUST NOT ever return a value less than the value returned
49/// by a previous call to`now()`.
50///
51/// Note that this means that timestamps returned by `now()` are expected
52/// not to overflow. Of course, all integers *will* overflow eventually, so
53/// this requirement can reasonably be weakened to expecting that timestamps
54/// returned by `now()` will not overflow unless the system has been running
55/// for a duration substantially longer than the system is expected to run
56/// for. For example, if a system is expected to run for as long as a year
57/// without being restarted, it's not unreasonable for timestamps returned
58/// by `now()` to overflow after, say, 100 years. Ideally, a general-purpose
59/// `Clock` implementation would not overflow for, say, 1,000 years.
60///
61/// The implication of this is that if the timestamp counters provided by
62/// the hardware platform are less than 64 bits wide (e.g., 16- or 32-bit
63/// timestamps), the `Clock` implementation is responsible for ensuring that
64/// they are extended to 64 bits, such as by counting overflows in the
65/// `Clock` implementation.
66///
67/// ## Examples
68///
69/// The simplest possible `Clock` implementation is one for a
70/// timestamp-counter-style hardware clock, where the timestamp counter is
71/// incremented on a fixed interval by the hardware. For such a counter, the
72/// `Clock` definition might look something like this:
73///
74///```rust
75/// use maitake::time::{Clock, Duration};
76/// # mod arch {
77/// #     pub fn read_timestamp_counter() -> u64 { 0 }
78/// #     pub const TIMESTAMP_COUNTER_FREQUENCY_HZ: u64 = 50_000_000;
79/// # }
80///
81/// // The fixed interval at which the timestamp counter is incremented.
82/// //
83/// // This is a made-up value; for real hardware, this value would be
84/// // determined from information provided by the hardware manufacturer,
85/// // and may need to be calculated based on the system's clock frequency.
86/// const TICK_DURATION: Duration = {
87///     let dur_ns = 1_000_000_000 / arch::TIMESTAMP_COUNTER_FREQUENCY_HZ;
88///     Duration::from_nanos(dur_ns)
89/// };
90///
91/// // Define a `Clock` implementation for the timestamp counter.
92/// let clock = Clock::new(TICK_DURATION, || {
93///     // A (pretend) function that reads the value of the timestamp
94///     // counter. In real life, this might be a specific instruction,
95///     // or a read from a timestamp counter register.
96///     arch::read_timestamp_counter()
97/// })
98///     // Adding a name to the clock definition allows it to be i
99///     // identified in fmt::Debug output.
100///     .named("timestamp-counter");
101///```
102///
103/// On some platforms, the frequency with which a timestamp counter is
104/// incremented may be configured by setting a divisor that divides the base
105/// frequency of the clock. On such a platform, it is possible to select the
106/// tick duration when constructing a new `Clock`. We could then provide a
107/// function that returns a clock with a requested tick duration:
108///
109///```rust
110/// use maitake::time::{Clock, Duration};
111/// # mod arch {
112/// #     pub fn read_timestamp_counter() -> u64 { 0 }
113/// #     pub fn set_clock_divisor(divisor: u32) {}
114/// #     pub const CLOCK_BASE_DURATION_NS: u32 = 100;
115/// # }
116///
117/// fn new_clock(tick_duration: Duration) -> Clock {
118///     // Determine the divisor necessary to achieve the requested tick
119///     // duration, based on the hardware clock's base frequency.
120///     let divisor = {
121///         let duration_ns = tick_duration.as_nanos();
122///         assert!(
123///             duration_ns as u32 >= arch::CLOCK_BASE_DURATION_NS,
124///             "tick duration too short"
125///         );
126///         let div_u128 = duration_ns / arch::CLOCK_BASE_DURATION_NS as u128;
127///         u32::try_from(div_u128).expect("tick duration too long")
128///     };
129///
130///     // Set the divisor to the hardware clock. On real hardware, this
131///     // might be a write to a register or memory-mapped IO location
132///     // that controls the clock's divisor.
133///     arch::set_clock_divisor(divisor as u32);
134///
135///     // Define a `Clock` implementation for the timestamp counter.
136///     Clock::new(tick_duration, arch::read_timestamp_counter)
137///         .named("timestamp-counter")
138/// }
139///```
140///
141/// In addition to timestamp-counter-based hardware clocks, a `Clock` definition
142/// can be provided for an interrupt-based hardware clock. In this case, we
143/// would provide an interrupt handler for the hardware timer interrupt that
144/// increments an [`AtomicU64`](core::sync::atomic::AtomicU64) counter, and a
145/// `now()` function that reads the counter. Essentially, we are reimplementing
146/// a hardware timestamp counter in software, using the hardware timer interrupt
147/// to increment the counter. For example:
148///
149/// ```rust
150/// use maitake::time::{Clock, Duration};
151/// use core::sync::atomic::{AtomicU64, Ordering};
152/// # mod arch {
153/// #    pub fn start_periodic_timer() -> u64 { 0 }
154/// #    pub fn set_timer_period_ns(_: u64) {}
155/// #    pub fn set_interrupt_handler(_: Interrupt, _: fn()) {}
156/// #    pub enum Interrupt { Timer }
157/// # }
158///
159/// // A counter that is incremented by the hardware timer interrupt.
160/// static CLOCK_TICKS: AtomicU64 = AtomicU64::new(0);
161///
162/// // The hardware timer interrupt handler.
163/// fn timer_interrupt_handler() {
164///     // Increment the counter.
165///     CLOCK_TICKS.fetch_add(1, Ordering::Relaxed);
166/// }
167///
168/// // Returns the current timestamp by reading the counter.
169/// fn now() -> u64 {
170///     CLOCK_TICKS.load(Ordering::Relaxed)
171/// }
172///
173/// fn new_clock(tick_duration: Duration) -> Clock {
174///     // Set the hardware timer to generate periodic interrupts.
175///     arch::set_timer_period_ns(tick_duration.as_nanos() as u64);
176///
177///     // Set the interrupt handler for the hardware timer interrupt,
178///     // and start the timer.
179///     arch::set_interrupt_handler(arch::Interrupt::Timer, timer_interrupt_handler);
180///     arch::start_periodic_timer();
181///
182///    // Define a `Clock` implementation for the interrupt-based timer.
183///    Clock::new(tick_duration, now)
184///         .named("periodic-timer")
185/// }
186/// ```
187/// [`std::time::Instant`]: https://doc.rust-lang.org/std/time/struct.Instant.html
188/// [monotonic]: https://en.wikipedia.org/wiki/Monotonic_function
189#[derive(Clone, Debug)]
190pub struct Clock {
191    now: fn() -> Ticks,
192    tick_duration: Duration,
193    name: &'static str,
194}
195
196/// A measurement of a monotonically nondecreasing [`Clock`].
197/// Opaque and useful only with [`Duration`].
198///
199/// Provided that the [`Clock`] implementation is correct, `Instant`s are always
200/// guaranteed to be no less than any previously measured instant when created,
201/// and are often useful for tasks such as measuring benchmarks or timing how
202/// long an operation takes.
203///
204/// Note, however, that instants are **not** guaranteed to be **steady**. In other
205/// words, each tick of the underlying clock might not be the same length (e.g.
206/// some seconds may be longer than others). An instant may jump forwards or
207/// experience time dilation (slow down or speed up), but it will never go
208/// backwards.
209/// As part of this non-guarantee it is also not specified whether system suspends count as
210/// elapsed time or not. The behavior varies across platforms and rust versions.
211#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
212pub struct Instant(Duration);
213
214/// [`Clock`] ticks are always counted by a 64-bit unsigned integer.
215pub type Ticks = u64;
216
217impl Clock {
218    /// Returns a new [`Clock`] with the provided tick [`Duration`] and `now()`
219    /// function.
220    ///
221    /// See the [type-level documentation for `Clock`](Self#implementing-now)
222    /// for details on implementing the `now()` function.
223    #[must_use]
224    pub const fn new(tick_duration: Duration, now: fn() -> Ticks) -> Self {
225        Self {
226            now,
227            tick_duration,
228            name: "<unnamed mystery clock>",
229        }
230    }
231
232    /// Add an arbitrary user-defined name to this `Clock`.
233    ///
234    /// This is generally used to describe the hardware time source used by the
235    /// `now()` function for this `Clock`.
236    #[must_use]
237    pub const fn named(self, name: &'static str) -> Self {
238        Self { name, ..self }
239    }
240
241    /// Returns the current `now` timestamp, in [`Ticks`] of this clock's base
242    /// tick duration.
243    #[must_use]
244    pub(crate) fn now_ticks(&self) -> Ticks {
245        (self.now)()
246    }
247
248    /// Returns the [`Duration`] of one tick of this clock.
249    #[must_use]
250    pub fn tick_duration(&self) -> Duration {
251        self.tick_duration
252    }
253
254    /// Returns an [`Instant`] representing the current timestamp according to
255    /// this [`Clock`].
256    #[must_use]
257    pub(crate) fn now(&self) -> Instant {
258        let now = self.now_ticks();
259        let tick_duration = self.tick_duration();
260        Instant(ticks_to_dur(tick_duration, now))
261    }
262
263    /// Returns the maximum duration of this clock.
264    #[must_use]
265    pub fn max_duration(&self) -> Duration {
266        max_duration(self.tick_duration())
267    }
268
269    /// Returns this `Clock`'s name, if it was given one using the [`Clock::named`]
270    /// method.
271    #[must_use]
272    pub fn name(&self) -> &'static str {
273        self.name
274    }
275}
276
277#[track_caller]
278#[inline]
279#[must_use]
280pub(in crate::time) fn ticks_to_dur(tick_duration: Duration, ticks: Ticks) -> Duration {
281    const NANOS_PER_SEC: u32 = 1_000_000_000;
282    // Multiply nanoseconds as u64, because it cannot overflow that way.
283    let total_nanos = tick_duration.subsec_nanos() as u64 * ticks;
284    let extra_secs = total_nanos / (NANOS_PER_SEC as u64);
285    let nanos = (total_nanos % (NANOS_PER_SEC as u64)) as u32;
286    let Some(secs) = tick_duration.as_secs().checked_mul(ticks) else {
287        panic!(
288            "ticks_to_dur({tick_duration:?}, {ticks}): multiplying tick \
289            duration seconds by ticks would overflow"
290        );
291    };
292    let Some(secs) = secs.checked_add(extra_secs) else {
293        panic!("ticks_to_dur({tick_duration:?}, {ticks}): extra seconds from nanos ({extra_secs}s) would overflow total seconds")
294    };
295    debug_assert!(nanos < NANOS_PER_SEC);
296    Duration::new(secs, nanos)
297}
298
299#[track_caller]
300#[inline]
301pub(in crate::time) fn dur_to_ticks(
302    tick_duration: Duration,
303    dur: Duration,
304) -> Result<Ticks, TimerError> {
305    (dur.as_nanos() / tick_duration.as_nanos())
306        .try_into()
307        .map_err(|_| TimerError::DurationTooLong {
308            requested: dur,
309            max: max_duration(tick_duration),
310        })
311}
312
313#[track_caller]
314#[inline]
315#[must_use]
316pub(in crate::time) fn max_duration(tick_duration: Duration) -> Duration {
317    tick_duration.saturating_mul(u32::MAX)
318}
319
320impl Instant {
321    /// Returns an instant corresponding to "now".
322    ///
323    /// This function uses the [global default timer][global]. See [the
324    /// module-level documentation][global] for details on using the global
325    /// default timer.
326    ///
327    /// # Panics
328    ///
329    /// This function panics if the [global default timer][global] has not been
330    /// set.
331    ///
332    /// For a version of this function that returns a [`Result`] rather than
333    /// panicking, use [`Instant::try_now`] instead.
334    ///
335    /// [global]: crate::time#global-timers
336    #[must_use]
337    pub fn now() -> Instant {
338        Self::try_now().expect("no global timer set")
339    }
340
341    /// Returns an instant corresponding to "now", without panicking.
342    ///
343    /// This function uses the [global default timer][global]. See [the
344    /// module-level documentation][global] for details on using the global
345    /// default timer.
346    ///
347    /// # Returns
348    ///
349    /// - [`Ok`]`(`[`Instant`]`)` if the [global default timer] is available.
350    /// - [`Err`]`(`[`TimerError`]`)` if no [global default timer][global] has
351    ///   been set.
352    ///
353    /// [global]: crate::time#global-timers
354    pub fn try_now() -> Result<Self, TimerError> {
355        Ok(timer::global::default()?.now())
356    }
357
358    /// Returns the amount of time elapsed from another instant to this one,
359    /// or zero duration if that instant is later than this one.
360    #[must_use]
361    pub fn duration_since(&self, earlier: Instant) -> Duration {
362        self.checked_duration_since(earlier).unwrap_or_default()
363    }
364
365    /// Returns the amount of time elapsed from another instant to this one,
366    /// or [`None`]` if that instant is later than this one.
367    #[must_use]
368    pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
369        self.0.checked_sub(earlier.0)
370    }
371
372    /// Returns the amount of time elapsed since this instant.
373    #[must_use]
374    pub fn elapsed(&self) -> Duration {
375        self.0
376    }
377
378    /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as
379    /// `Instant` (which means it's inside the bounds of the underlying data structure), [`None`]
380    /// otherwise.
381    #[must_use]
382    pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
383        self.0.checked_add(duration).map(Instant)
384    }
385
386    /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as
387    /// `Instant` (which means it's inside the bounds of the underlying data structure), [`None`]
388    /// otherwise.
389    #[must_use]
390    pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
391        self.0.checked_sub(duration).map(Instant)
392    }
393}
394
395impl Add<Duration> for Instant {
396    type Output = Instant;
397
398    /// # Panics
399    ///
400    /// This function may panic if the resulting point in time cannot be represented by the
401    /// underlying data structure. See [`Instant::checked_add`] for a version without panic.
402    fn add(self, other: Duration) -> Instant {
403        self.checked_add(other)
404            .expect("overflow when adding duration to instant")
405    }
406}
407
408impl AddAssign<Duration> for Instant {
409    fn add_assign(&mut self, other: Duration) {
410        *self = *self + other;
411    }
412}
413
414impl Sub<Duration> for Instant {
415    type Output = Instant;
416
417    fn sub(self, other: Duration) -> Instant {
418        self.checked_sub(other)
419            .expect("overflow when subtracting duration from instant")
420    }
421}
422
423impl SubAssign<Duration> for Instant {
424    fn sub_assign(&mut self, other: Duration) {
425        *self = *self - other;
426    }
427}
428
429impl Sub<Instant> for Instant {
430    type Output = Duration;
431
432    /// Returns the amount of time elapsed from another instant to this one,
433    /// or zero duration if that instant is later than this one.
434    fn sub(self, other: Instant) -> Duration {
435        self.duration_since(other)
436    }
437}
438
439impl fmt::Display for Instant {
440    #[inline]
441    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
442        fmt::Debug::fmt(&self.0, f)
443    }
444}