Struct maitake::time::clock::Clock

source ·
pub struct Clock { /* private fields */ }
Expand description

A hardware clock definition.

A Clock consists of a function that returns the hardware clock’s current timestamp in Ticks (now()), and a Duration that defines the amount of time represented by a single tick of the clock.

Using Clocks

A Clock must be provided when constructing a Timer. The Clock provided to Timer::new is used to determine the time to advance to when turning the timer wheel, and to determine the start time when adding a Sleep future to the timer wheel.

In addition, once a global timer is set using the set_global_timer function, the Instant::now() function may be used to produce Instants representing the current timestamp according to that timer’s Clock. The Instant type is analogous to the std::time::Instant type in the Rust standard library, and may be used to compare the time elapsed between two events, as a timestamp for logs and other diagnostics, and similar purposes.

Implementing now()

Constructing a new Clock definition takes a function, called now(), that returns the current hardware timestamp in a 64-bit number of, ticks. The period of time represented by a tick is indicated by the tick_duration argument to Clock::new. In order to define a Clock representing a particular hardware time source, a now() function must be implemented using that time source.

Monotonicity

Implementations of now() MUST ensure that timestamps returned by now() MUST be monontonically non-decreasing. This means that a call to now() MUST NOT ever return a value less than the value returned by a previous call tonow().

Note that this means that timestamps returned by now() are expected not to overflow. Of course, all integers will overflow eventually, so this requirement can reasonably be weakened to expecting that timestamps returned by now() will not overflow unless the system has been running for a duration substantially longer than the system is expected to run for. For example, if a system is expected to run for as long as a year without being restarted, it’s not unreasonable for timestamps returned by now() to overflow after, say, 100 years. Ideally, a general-purpose Clock implementation would not overflow for, say, 1,000 years.

The implication of this is that if the timestamp counters provided by the hardware platform are less than 64 bits wide (e.g., 16- or 32-bit timestamps), the Clock implementation is responsible for ensuring that they are extended to 64 bits, such as by counting overflows in the Clock implementation.

Examples

The simplest possible Clock implementation is one for a timestamp-counter-style hardware clock, where the timestamp counter is incremented on a fixed interval by the hardware. For such a counter, the Clock definition might look something like this:

 use maitake::time::{Clock, Duration};

 // The fixed interval at which the timestamp counter is incremented.
 //
 // This is a made-up value; for real hardware, this value would be
 // determined from information provided by the hardware manufacturer,
 // and may need to be calculated based on the system's clock frequency.
 const TICK_DURATION: Duration = {
     let dur_ns = 1_000_000_000 / arch::TIMESTAMP_COUNTER_FREQUENCY_HZ;
     Duration::from_nanos(dur_ns)
 };

 // Define a `Clock` implementation for the timestamp counter.
 let clock = Clock::new(TICK_DURATION, || {
     // A (pretend) function that reads the value of the timestamp
     // counter. In real life, this might be a specific instruction,
     // or a read from a timestamp counter register.
     arch::read_timestamp_counter()
 })
     // Adding a name to the clock definition allows it to be i
     // identified in fmt::Debug output.
     .named("timestamp-counter");

On some platforms, the frequency with which a timestamp counter is incremented may be configured by setting a divisor that divides the base frequency of the clock. On such a platform, it is possible to select the tick duration when constructing a new Clock. We could then provide a function that returns a clock with a requested tick duration:

 use maitake::time::{Clock, Duration};

 fn new_clock(tick_duration: Duration) -> Clock {
     // Determine the divisor necessary to achieve the requested tick
     // duration, based on the hardware clock's base frequency.
     let divisor = {
         let duration_ns = tick_duration.as_nanos();
         assert!(
             duration_ns as u32 >= arch::CLOCK_BASE_DURATION_NS,
             "tick duration too short"
         );
         let div_u128 = duration_ns / arch::CLOCK_BASE_DURATION_NS as u128;
         u32::try_from(div_u128).expect("tick duration too long")
     };

     // Set the divisor to the hardware clock. On real hardware, this
     // might be a write to a register or memory-mapped IO location
     // that controls the clock's divisor.
     arch::set_clock_divisor(divisor as u32);

     // Define a `Clock` implementation for the timestamp counter.
     Clock::new(tick_duration, arch::read_timestamp_counter)
         .named("timestamp-counter")
 }

In addition to timestamp-counter-based hardware clocks, a Clock definition can be provided for an interrupt-based hardware clock. In this case, we would provide an interrupt handler for the hardware timer interrupt that increments an AtomicU64 counter, and a now() function that reads the counter. Essentially, we are reimplementing a hardware timestamp counter in software, using the hardware timer interrupt to increment the counter. For example:

use maitake::time::{Clock, Duration};
use core::sync::atomic::{AtomicU64, Ordering};

// A counter that is incremented by the hardware timer interrupt.
static CLOCK_TICKS: AtomicU64 = AtomicU64::new(0);

// The hardware timer interrupt handler.
fn timer_interrupt_handler() {
    // Increment the counter.
    CLOCK_TICKS.fetch_add(1, Ordering::Relaxed);
}

// Returns the current timestamp by reading the counter.
fn now() -> u64 {
    CLOCK_TICKS.load(Ordering::Relaxed)
}

fn new_clock(tick_duration: Duration) -> Clock {
    // Set the hardware timer to generate periodic interrupts.
    arch::set_timer_period_ns(tick_duration.as_nanos() as u64);

    // Set the interrupt handler for the hardware timer interrupt,
    // and start the timer.
    arch::set_interrupt_handler(arch::Interrupt::Timer, timer_interrupt_handler);
    arch::start_periodic_timer();

   // Define a `Clock` implementation for the interrupt-based timer.
   Clock::new(tick_duration, now)
        .named("periodic-timer")
}

Implementations§

source§

impl Clock

source

pub const fn new(tick_duration: Duration, now: fn() -> Ticks) -> Self

Returns a new Clock with the provided tick Duration and now() function.

See the type-level documentation for Clock for details on implementing the now() function.

source

pub const fn named(self, name: &'static str) -> Self

Add an arbitrary user-defined name to this Clock.

This is generally used to describe the hardware time source used by the now() function for this Clock.

source

pub fn tick_duration(&self) -> Duration

Returns the Duration of one tick of this clock.

source

pub fn max_duration(&self) -> Duration

Returns the maximum duration of this clock.

source

pub fn name(&self) -> &'static str

Returns this Clock’s name, if it was given one using the Clock::named method.

Trait Implementations§

source§

impl Clone for Clock

source§

fn clone(&self) -> Clock

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Clock

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl RefUnwindSafe for Clock

§

impl Send for Clock

§

impl Sync for Clock

§

impl Unpin for Clock

§

impl UnwindSafe for Clock

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> ToOwned for T
where T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more