Struct maitake::time::Timer

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

A Timer tracks the current time, and notifies Sleep and Timeout futures when they complete.

This timer implementation uses a hierarchical timer wheel to track large numbers of Sleep futures efficiently.

Creating Futures

A Timer instance is necessary to create Sleep and Timeout futures. Once a Sleep or Timeout future is created by a Timer, they are bound to that Timer instance, and will be woken by the Timer once it advances past the deadline for that future.

The Timer::sleep and Timer::timeout methods create Sleep and Timeout futures, respectively. In addition, fallible Timer::try_sleep and Timer::try_timeout methods are available, which do not panic on invalid durations. These methods may be used in systems where panicking must be avoided.

Setting a Global Timer

In addition to creating Sleep and Timeout futures using methods on a Timer instance, a timer may also be set as a [global default timer]. This allows the use of the free functions sleep, timeout, try_sleep, and try_timeout, which do not require a reference to a Timer to be passed in. See the documentation on global timers for details.

Driving Timers

⚠️ A timer wheel at rest will remain at rest unless acted upon by an outside force!

Since maitake is intended for bare-metal platforms without an operating system, a Timer instance cannot automatically advance time. Instead, the operating system’s run loop must periodically call the Timer::turn method, which advances the timer wheel to the current time and wakes any futures whose timers have completed.

The Timer::turn method may be called on a periodic interrupt, or on every iteration of a system run loop. If the system is also using the maitake::scheduler module, calling Timer::turn after a call to Scheduler::tick is generally appropriate.

Clocks

In addition to driving the timer, user code must also provide a Clock when constructing a Timer. The Clock is a representation of a hardware time source used to determine the current timestamp. See the Clock type’s documentation for details on implementing clocks.

Hardware Time Sources

Depending on the hardware platform, a time source may be a timer interrupt that fires on a known interval1, or a timestamp that’s read by reading from a special register2, a memory-mapped IO location, or by executing a special instruction3. A combination of multiple time sources can also be used.

Periodic and One-Shot Timer Interrupts

Generally, hardware timer interrupts operate in one of two modes: periodic timers, which fire on a regular interval, and one-shot timers, where the timer counts down to a particular time, fires the interrupt, and then stops until it is reset by software. Depending on the particular hardware platform, one or both of these timer modes may be available.

Using a periodic timer with the maitake timer wheel is quite simple: construct the timer wheel with the minimum granularity set to the period of the timer interrupt, and call Timer::try_turn every time the periodic interrupt occurs.

However, if the hardware platform provides a way to put the processor in a low-power state while waiting for an interrupt, it may be desirable to instead use a one-shot timer mode. When a timer wheel is advanced, it returns a Turn structure describing what happened while advancing the wheel. Among other things, this includes the duration until the next scheduled timer expires. If the timer is advanced when the system has no other work to perform, and no new work was scheduled as a result of advancing the timer wheel to the current time, the system can then instruct the one-shot timer to fire in Turn::time_to_next_deadline, and put the processor in a low-power state to wait for that interrupt. This allows the system to idle more efficiently than if it was woken repeatedly by a periodic timer interrupt.

Timestamp-Driven Timers

When the timer is advanced by reading from a time source, the Timer::turn method should be called periodically as part of a runtime loop (as discussed in he previous section), such as every time the scheduler is ticked. Advancing the timer more frequently will result in Sleep futures firing with a higher resolution, while less frequent calls to Timer::turn will result in more noise in when Sleep futures actually complete.

Timer Granularity

Within the timer wheel, the duration of a Sleep future is represented as a number of abstract “timer ticks”. The actual duration in real life time that’s represented by a number of ticks depends on the timer’s _granularity.

When constructing Timer (e.g. by calling Timer::new), the minimum granularity of the timer is determined by the Clock’s tick_duration value. The selected tick duration influences both the resolution of the timer (i.e. the minimum difference in duration between two Sleep futures that can be distinguished by the timer), and the maximum duration that can be represented by a Sleep future (which is limited by the size of a 64-bit integer).

A longer tick duration will allow represented longer sleeps, as the maximum allowable sleep is the timer’s granularity multiplied by u64::MAX. A shorter tick duration will allow for more precise sleeps at the expense of reducing the maximum allowed sleep.


  1. Such as the 8253 PIT interrupt on most x86 systems. 

  2. Such as the CCNT register on ARMv7. 

  3. Such as the rdtsc instruction on x86_64. 

Implementations§

source§

impl Timer

source

pub fn timeout<F: Future>( &self, duration: Duration, future: F ) -> Timeout<'_, F>

Returns a new Timeout future that fails if future does not complete within the specified duration.

The timeout will be driven by this timer.

Output
  • Ok(F::Output) if the inner future completed before the specified timeout.
  • Err(Elapsed) if the timeout elapsed before the inner Future completed.
Cancellation

Dropping a Timeout future cancels the timeout. The wrapped Future can be extracted from the Timeout future by calling Timeout::into_inner, allowing the future to be polled without failing if the timeout elapses.

Panics

This method panics if the provided duration exceeds the maximum sleep duration allowed this timer.

For a version of this method that does not panic, use the Timer::try_timeout method instead.

source

pub fn try_timeout<F: Future>( &self, duration: Duration, future: F ) -> Result<Timeout<'_, F>, TimerError>

Returns a new Timeout future that fails if future does not complete within the specified duration.

The timeout will be driven by this timer.

Returns
Output
  • Ok(F::Output) if the inner future completed before the specified timeout.
  • Err(Elapsed) if the timeout elapsed before the inner Future completed.
Cancellation

Dropping a Timeout future cancels the timeout. The wrapped Future can be extracted from the Timeout future by calling Timeout::into_inner, allowing the future to be polled without failing if the timeout elapses.

Panics

This method does not panic. For a version of this methodthat panics rather than returning a TimerError, use Timer::timeout.

source§

impl Timer

source

pub const fn new(clock: Clock) -> Self

Returns a new Timer with the specified hardware Clock.

source

pub fn now(&self) -> Instant

Returns the current timestamp according to this timer’s hardware Clock, as an Instant.

source

pub fn clock(&self) -> &Clock

Borrows the hardware Clock definition used by this timer.

source

pub fn max_duration(&self) -> Duration

Returns the maximum duration of Sleep futures driven by this timer.

source

pub fn sleep(&self, duration: Duration) -> Sleep<'_>

Returns a Future that will complete in duration.

Returns

The returned Sleep future will be driven by this timer, and will complete once this timer has advanced by at least duration.

Panics

This method panics if the provided duration exceeds the maximum sleep duration allowed by this timer.

For a version of this function that does not panic, see Timer::try_sleep.

source

pub fn try_sleep(&self, duration: Duration) -> Result<Sleep<'_>, TimerError>

Returns a Future that will complete in duration.

Returns

The returned Sleep future will be driven by this timer, and will complete once this timer has advanced by at least duration.

Panics

This method does not panic. For a version of this method that panics rather than returning an error, see Timer::sleep.

source

pub fn sleep_ticks(&self, ticks: Ticks) -> Sleep<'_>

Returns a Future that will complete in ticks timer ticks.

Returns

The returned Sleep future will be driven by this timer, and will complete once this timer has advanced by at least ticks timer ticks.

source

pub fn try_turn(&self) -> Option<Turn>

Attempt to turn the timer to the current now if the timer is not locked, potentially waking any Sleep futures that have completed.

Returns
Interrupt Safety

This method will never spin if the timer wheel lock is held; instead, it will add any new ticks to a counter of “pending” ticks and return immediately. Therefore, it is safe to call this method in an interrupt handler, as it will never acquire a lock that may already be locked.

The Timer::turn method will spin to lock the timer wheel lock if it is currently held, ensuring that any pending wakeups are processed. That method should never be called in an interrupt handler.

If a timer is driven primarily by calling try_turn in an interrupt handler, it may be desirable to occasionally call Timer::turn outside of an interrupt handler (i.e., as as part of an occasional runtime bookkeeping process). This ensures that any pending ticks are observed by the timer in a relatively timely manner.

source

pub fn turn(&self) -> Turn

Advance the timer to the current time, ensuring any Sleep futures that have completed are woken, even if a lock must be acquired.

Returns

A Turn structure describing what occurred during this turn of the wheel, including including the current time and the deadline of the next expiring timer, if one exists.

Interrupt Safety

This method will spin to acquire the timer wheel lock if it is currently held elsewhere. Therefore, this method must NEVER be called in an interrupt handler!

If a timer is advanced inside an interrupt handler, use the Timer::try_turn method instead. If a timer is advanced primarily by calls to Timer::try_turn in an interrupt handler, it may be desirable to occasionally call turn outside an interrupt handler, to ensure that pending ticks are drained frequently.

Trait Implementations§

source§

impl Debug for Timer

source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl !RefUnwindSafe for Timer

§

impl Send for Timer

§

impl Sync for Timer

§

impl Unpin for Timer

§

impl !UnwindSafe for Timer

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, 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