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.
Such as the 8253 PIT interrupt on most x86 systems. ↩
Such as the
CCNTregister on ARMv7. ↩Such as the
rdtscinstruction on x86_64. ↩
Implementations§
Source§impl Timer
impl Timer
Sourcepub fn timeout<F: Future>(
&self,
duration: Duration,
future: F,
) -> Timeout<'_, F> ⓘ
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 innerFuturecompleted.
§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.
Sourcepub fn try_timeout<F: Future>(
&self,
duration: Duration,
future: F,
) -> Result<Timeout<'_, F>, TimerError>
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
Ok(Timeout)if a newTimeoutfuture was created successfully.Err(TimerError::DurationTooLong)if the requested timeout duration exceeds this timer’s maximum sleep duration.
§Output
Ok(F::Output)if the inner future completed before the specified timeout.Err(Elapsed)if the timeout elapsed before the innerFuturecompleted.
§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
impl Timer
Sourcepub const fn new(tick_duration: Duration) -> Self
pub const fn new(tick_duration: Duration) -> Self
Returns a new Timer with the specified tick_duration for a single timer
tick.
Sourcepub fn max_duration(&self) -> Duration
pub fn max_duration(&self) -> Duration
Returns the maximum duration of Sleep futures driven by this timer.
Sourcepub fn sleep(&self, duration: Duration) -> Sleep<'_> ⓘ
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.
Sourcepub fn try_sleep(&self, duration: Duration) -> Result<Sleep<'_>, TimerError>
pub fn try_sleep(&self, duration: Duration) -> Result<Sleep<'_>, TimerError>
Returns a Future that will complete in duration.
§Returns
Ok(Sleep)if a newSleepfuture was created successfully.Err(TimerError::DurationTooLong)if the requested sleep duration exceeds this timer’s maximum sleep duration.
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.
Sourcepub fn sleep_ticks(&self, ticks: u64) -> Sleep<'_> ⓘ
pub fn sleep_ticks(&self, ticks: u64) -> Sleep<'_> ⓘ
Sourcepub fn pend_duration(&self, duration: Duration)
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.
Sourcepub fn pend_ticks(&self, ticks: u64)
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.
Sourcepub fn advance(&self, duration: Duration)
pub fn advance(&self, duration: Duration)
Advance the timer by duration, potentially waking any Sleep futures
that have completed.
§Returns
Some(Turn)if the lock was acquired and the wheel was advanced. ATurnstructure describes what occurred during this turn of the wheel, including the current time and the deadline of the next expiring timer, if one exists.Noneif the wheel was not advanced because the lock was already held.
§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.
Sourcepub fn advance_ticks(&self, ticks: u64)
pub fn advance_ticks(&self, ticks: u64)
Advance the timer by ticks timer ticks, potentially waking any Sleep
futures that have completed.
§Returns
Some(Turn)if the lock was acquired and the wheel was advanced. ATurnstructure describes what occurred during this turn of the wheel, including the current time and the deadline of the next expiring timer, if one exists.Noneif the wheel was not advanced because the lock was already held.
§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.
Sourcepub fn force_advance(&self, duration: Duration) -> Turn
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.
Sourcepub fn force_advance_ticks(&self, ticks: u64) -> Turn
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.