maitake::time

Struct 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, it must be driven by a time source, which calls the Timer::advance method and/or the Timer::pend_duration and Timer::force_advance methods.

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.

In any case, the timer must be advanced periodically by the time source.

§Interrupt-Driven Timers

When the timer is interrupt-driven, the interrupt handler for the timer interrupt should call either the Timer::pend_duration or Timer::advance methods.

Timer::advance will attempt to optimistically acquire a spinlock, and advance the timer if it is acquired, or add to the pending tick counter if the timer wheel is currently locked. Therefore, it is safe to call in an interrupt handler, as it and cannot cause a deadlock.

However, if interrupt handlers must be extremely short, the Timer::pend_duration method can be used, instead. This method will never acquire a lock, and does not actually turn the timer wheel. Instead, it always performs only a single atomic add. If the time source is an interrupt handler which calls Timer::pend_duration, though, the timer wheel must be turned externally. This can be done by calling the Timer::force_advance_ticks method periodically outside of the interrupt handler, with a duration of 0 ticks. In general, this should be done as some form of runtime bookkeeping action. For example, the timer can be advanced in a system’s run loop every time the Scheduler::tick method completes.

§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::advance_ticks(1) or Timer::pend_ticks(1) in the interrupt handler, as discused above.

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::advance method should generally be used to drive the timer. Prior to calling Timer::advance, the time source is read to determine the duration that has elapsed since the last time Timer::advance was called, and that duration is provided when calling advance.

This should occur periodically as part of a runtime loop (as discussed in the 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::advance 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 a Timer (e.g. by calling Timer::new), the minimum granularity of the timer is selected by providing the Duration represented by a single timer tick. 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.

When using an interrupt-driven time source, the tick duration should generally be the interval that the timer interrupt fires at. A finer resolution won’t have any benefit, as the timer only fires at that frequency, and all sleeps that complete between two timer interrupts will be woken at the same time anyway.

When using a timestamp-driven time source, selecting the resolution of the timestamp counter as the timer’s tick duration is probably a good choice.


  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(tick_duration: Duration) -> Self

Returns a new Timer with the specified tick_duration for a single timer tick.

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: u64) -> 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 pend_duration(&self, duration: Duration)

Add pending time to the timer without turning the wheel.

This function will never acquire a lock, and will never notify any waiting Sleep futures. It can be called in an interrupt handler that cannot perform significant amounts of work.

However, if this method is used, then Timer::force_advance must be called frequently from outside of the interrupt handler.

Source

pub fn pend_ticks(&self, ticks: u64)

Add pending ticks to the timer without turning the wheel.

This function will never acquire a lock, and will never notify any waiting Sleep futures. It can be called in an interrupt handler that cannot perform significant amounts of work.

However, if this method is used, then Timer::force_advance must be called frequently from outside of the interrupt handler.

Source

pub fn advance(&self, duration: Duration)

Advance the timer by duration, 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 force_advance 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 advance in an interrupt handler, it may be desirable to occasionally call force_advance 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 advance_ticks(&self, ticks: u64)

Advance the timer by ticks timer ticks, 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 force_advance_ticks 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 advance in an interrupt handler, it may be desirable to occasionally call force_advance_ticks 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 force_advance(&self, duration: Duration) -> Turn

Advance the timer by duration, 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 advance method instead. If a timer is advanced primarily by calls to advance, it may be desirable to occasionally call force_advance outside an interrupt handler, to ensure that pending ticks are drained frequently.

Source

pub fn force_advance_ticks(&self, ticks: u64) -> Turn

Advance the timer by ticks timer ticks, 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 advance_ticks method instead. If a timer is advanced primarily by calls to advance_ticks, it may be desirable to occasionally call force_advance 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 !Freeze for Timer

§

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.

Source§

impl<T> Instrument for T

Source§

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

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

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

Source§

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

Source§

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.