maitake/time/timer.rs
1//! A [`Timer`] tracks the current time, and notifies [`Sleep`] and [`Timeout`]
2//! [future]s when they complete.
3//!
4//! See the [`Timer`] type's documentation for details.
5//!
6//! [`Sleep`]: crate::time::Sleep
7//! [`Timeout`]: crate::time::Timeout
8//! [future]: core::future::Future
9use super::clock::{self, Clock, Instant, Ticks};
10use crate::{
11 loom::sync::atomic::{AtomicUsize, Ordering::*},
12 util::expect_display,
13};
14use core::time::Duration;
15use maitake_sync::blocking::Mutex;
16use mycelium_util::fmt;
17
18#[cfg(test)]
19mod tests;
20
21pub(super) mod global;
22pub(super) mod sleep;
23mod wheel;
24
25pub use self::global::{set_global_timer, AlreadyInitialized};
26use self::sleep::Sleep;
27
28/// A `Timer` tracks the current time, and notifies [`Sleep`] and [`Timeout`]
29/// [future]s when they complete.
30///
31/// This timer implementation uses a [hierarchical timer wheel][wheel] to track
32/// large numbers of `Sleep` futures efficiently.
33///
34/// # Creating Futures
35///
36/// A `Timer` instance is necessary to create [`Sleep`] and [`Timeout`] futures.
37/// Once a [`Sleep`] or [`Timeout`] future is created by a `Timer`, they are
38/// *bound* to that `Timer` instance, and will be woken by the `Timer` once it
39/// advances past the deadline for that future.
40///
41/// The [`Timer::sleep`] and [`Timer::timeout`] methods create [`Sleep`] and
42/// [`Timeout`] futures, respectively. In addition, fallible
43/// [`Timer::try_sleep`] and [`Timer::try_timeout`] methods are available, which
44/// do not panic on invalid durations. These methods may be used in systems
45/// where panicking must be avoided.
46///
47/// ### Setting a Global Timer
48///
49/// In addition to creating [`Sleep`] and [`Timeout`] futures using methods on a
50/// `Timer` instance, a timer may also be set as a [global default timer]. This
51/// allows the use of the free functions [`sleep`], [`timeout`],
52/// [`try_sleep`], and [`try_timeout`], which do not require a reference to a
53/// `Timer` to be passed in. See [the documentation on global timers][global]
54/// for details.
55///
56/// # Driving Timers
57///
58/// ⚠️ *A timer wheel at rest will remain at rest unless acted
59/// upon by an outside force!*
60///
61/// Since `maitake` is intended for bare-metal platforms without an operating
62/// system, a `Timer` instance cannot automatically advance time. Instead, the
63/// operating system's run loop must periodically call the [`Timer::turn`]
64/// method, which advances the timer wheel to the current time and wakes any
65/// futures whose timers have completed.
66///
67/// The [`Timer::turn`] method may be called on a periodic interrupt, or on
68/// every iteration of a system run loop. If the system is also using the
69/// [`maitake::scheduler`](crate::scheduler) module, calling [`Timer::turn`]
70/// after a call to [`Scheduler::tick`] is generally appropriate.
71///
72/// # Clocks
73///
74/// In addition to driving the timer, user code must also provide a [`Clock`]
75/// when constructing a [`Timer`]. The [`Clock`] is a representation of a
76/// hardware time source used to determine the current timestamp. See the
77/// [`Clock`] type's documentation for details on implementing clocks.
78///
79/// ## Hardware Time Sources
80///
81/// Depending on the hardware platform, a time source may be a timer interrupt
82/// that fires on a known interval[^1], or a timestamp that's read by reading
83/// from a special register[^2], a memory-mapped IO location, or by executing a
84/// special instruction[^3]. A combination of multiple time sources can also be
85/// used.
86///
87/// [^1]: Such as the [8253 PIT interrupt] on most x86 systems.
88///
89/// [^2]: Such as the [`CCNT` register] on ARMv7.
90///
91/// [^3]: Such as the [`rdtsc` instruction] on x86_64.
92///
93/// ### Periodic and One-Shot Timer Interrupts
94///
95/// Generally, hardware timer interrupts operate in one of two modes: _periodic_
96/// timers, which fire on a regular interval, and _one-shot_ timers, where the
97/// timer counts down to a particular time, fires the interrupt, and then stops
98/// until it is reset by software. Depending on the particular hardware
99/// platform, one or both of these timer modes may be available.
100///
101/// Using a periodic timer with the `maitake` timer wheel is quite simple:
102/// construct the timer wheel with the minimum [granularity](#timer-granularity)
103/// set to the period of the timer interrupt, and call [`Timer::try_turn`] every
104/// time the periodic interrupt occurs.
105///
106/// However, if the hardware platform provides a way to put the processor in a
107/// low-power state while waiting for an interrupt, it may be desirable to
108/// instead use a one-shot timer mode. When a timer wheel is advanced, it
109/// returns a [`Turn`] structure describing what happened while advancing the
110/// wheel. Among other things, this includes [the duration until the next
111/// scheduled timer expires](Turn::time_to_next_deadline). If the timer is
112/// advanced when the system has no other work to perform, and no new work was
113/// scheduled as a result of advancing the timer wheel to the current time, the
114/// system can then instruct the one-shot timer to fire in
115/// [`Turn::time_to_next_deadline`], and put the processor in a low-power state
116/// to wait for that interrupt. This allows the system to idle more efficiently
117/// than if it was woken repeatedly by a periodic timer interrupt.
118///
119/// ### Timestamp-Driven Timers
120///
121/// When the timer is advanced by reading from a time source, the
122/// [`Timer::turn`] method should be called periodically as part of a runtime
123/// loop (as discussed in he previous section), such as every time [the
124/// scheduler is ticked][`Scheduler::tick`]. Advancing the timer more frequently
125/// will result in [`Sleep`] futures firing with a higher resolution, while less
126/// frequent calls to [`Timer::turn`] will result in more noise in when [`Sleep`]
127/// futures actually complete.
128///
129/// ## Timer Granularity
130///
131/// Within the timer wheel, the duration of a [`Sleep`] future is represented as
132/// a number of abstract "timer ticks". The actual duration in real life time
133/// that's represented by a number of ticks depends on the timer's _granularity.
134///
135/// When constructing `Timer` (e.g. by calling [`Timer::new`]), the minimum
136/// granularity of the timer is determined by the [`Clock`]'s `tick_duration`
137/// value. The selected tick duration influences both the resolution of the
138/// timer (i.e. the minimum difference in duration between two `Sleep` futures
139/// that can be distinguished by the timer), and the maximum duration that can
140/// be represented by a `Sleep` future (which is limited by the size of a 64-bit
141/// integer).
142///
143/// A longer tick duration will allow represented longer sleeps, as the maximum
144/// allowable sleep is the timer's granularity multiplied by [`u64::MAX`]. A
145/// shorter tick duration will allow for more precise sleeps at the expense of
146/// reducing the maximum allowed sleep.
147///
148/// [`Sleep`]: crate::time::Sleep
149/// [`Timeout`]: crate::time::Timeout
150/// [future]: core::future::Future
151/// [wheel]: http://www.cs.columbia.edu/~nahum/w6998/papers/sosp87-timing-wheels.pdf
152/// [8253 PIT interrupt]: https://en.wikipedia.org/wiki/Intel_8253#IBM_PC_programming_tips_and_hints
153/// [`CCNT` register]: https://developer.arm.com/documentation/ddi0211/h/system-control-coprocessor/system-control-processor-register-descriptions/c15--cycle-counter-register--ccnt-
154/// [`rdtsc` instruction]: https://www.felixcloutier.com/x86/rdtsc
155/// [`Scheduler::tick`]: crate::scheduler::Scheduler::tick
156/// [`sleep`]: crate::time::sleep()
157/// [`timeout`]: crate::time::timeout()
158/// [`try_sleep`]: crate::time::try_sleep()
159/// [`try_timeout`]: crate::time::try_timeout()
160/// [global]: crate::time#global-timers
161pub struct Timer {
162 clock: Clock,
163
164 /// A count of how many timer ticks have elapsed since the last time the
165 /// timer's [`Core`] was updated.
166 ///
167 /// The timer's [`advance`] method may be called in an interrupt
168 /// handler, so it cannot spin to lock the `Core` if it is busy. Instead, it
169 /// tries to acquire the [`Core`] lock, and if it can't, it increments
170 /// `pending_ticks`. The count of pending ticks is then consumed the next
171 /// time the timer interrupt is able to lock the [`Core`].
172 ///
173 /// This strategy may result in some additional noise in when exactly a
174 /// sleep will fire, but it allows us to avoid potential deadlocks when the
175 /// timer is advanced from an interrupt handler.
176 ///
177 /// [`Core`]: wheel::Core
178 /// [`advance`]: Timer::advance
179 pending_ticks: AtomicUsize,
180
181 /// The hierarchical timer wheel.
182 ///
183 /// This is stored inside a [`Mutex`], which must be locked in order to
184 /// register a new [`Sleep`] future, cancel a [`Sleep`] that is currently
185 /// registered, and turn the timer wheel. `pending_ticks` is outside this
186 /// lock, in order to allow incrementing the time that has advanced in an
187 /// ISR without locking. Pending ticks are then consumed by locking the
188 /// wheel and advancing the current time, which can be done either
189 /// optimistically (*try* to consume any pending ticks when the lock is not
190 /// busy), or forcefully (spin to acquire the lock and then ensure all
191 /// pending ticks are *definitely* consumed).
192 ///
193 /// XXX(eliza): would be nice if the "elapsed" counter could be moved
194 /// outside the lock so we can have a global `Instant::now()` without
195 /// locking...but that's hard to do without 64-bit atomics...
196 ///
197 /// Also, we could consider trying to make locking more granular here so
198 /// that we lock individual wheels in order to register a sleep, but that
199 /// would be Complicated...and then we'd need some way to tell a sleep
200 /// future "you are on a different wheel now, so here is what you would have
201 /// to lock if you need to cancel yourself"...
202 core: Mutex<wheel::Core>,
203}
204
205/// Represents a single turn of the timer wheel.
206#[derive(Debug)]
207pub struct Turn {
208 /// The total number of ticks elapsed since the first time this timer wheel was
209 /// advanced.
210 pub now: Ticks,
211
212 /// The tick at which the next deadline in the timer wheel expires.
213 ///
214 /// If this is `None`, there are currently no scheduled [`Sleep`] futures in the
215 /// wheel.
216 next_deadline_ticks: Option<Ticks>,
217
218 /// The number of [`Sleep`] futures that were woken up by this turn of the
219 /// timer wheel.
220 pub expired: usize,
221
222 /// The timer's tick duration (granularity); used for converting `now_ticks`
223 /// and `next_deadline_ticks` into [`Duration`].
224 tick_duration: Duration,
225}
226
227/// Errors returned by [`Timer::try_sleep`], [`Timer::try_timeout`], and the
228/// global [`try_sleep`] and [`try_timeout`] functions.
229///
230/// [`try_sleep`]: super::try_sleep
231/// [`try_timeout`]: super::try_timeout
232#[derive(Debug, Eq, PartialEq)]
233#[non_exhaustive]
234pub enum TimerError {
235 /// No [global default timer][global] has been set.
236 ///
237 /// This error is returned by the [`try_sleep`] and [`try_timeout`]
238 /// functions only.
239 ///
240 /// [global]: super#global-timers
241 /// [`try_sleep`]: super::try_sleep
242 /// [`try_timeout`]: super::try_timeout
243 NoGlobalTimer,
244 /// The requested [`Duration`] exceeds the [timer's maximum duration][max].
245 ///
246 /// This error is returned by [`Timer::try_sleep`], [`Timer::try_timeout`],
247 /// and the global [`try_sleep`] and [`try_timeout`] functions.
248 ///
249 /// [`try_sleep`]: super::try_sleep
250 /// [`try_timeout`]: super::try_timeout
251 /// [max]: Timer::max_duration
252 DurationTooLong {
253 /// The duration that was requested for a [`Sleep`] or [`Timeout`]
254 /// future.
255 ///
256 /// [`Timeout`]: crate::time::Timeout
257 requested: Duration,
258 /// The [maximum duration][max] supported by this [`Timer`] instance.
259 ///
260 /// [max]: Timer::max_duration
261 max: Duration,
262 },
263}
264
265// === impl Timer ===
266
267impl Timer {
268 loom_const_fn! {
269 /// Returns a new `Timer` with the specified hardware [`Clock`].
270 #[must_use]
271 pub fn new(clock: Clock) -> Self {
272 Self {
273 clock,
274 pending_ticks: AtomicUsize::new(0),
275 core: Mutex::new(wheel::Core::new()),
276 }
277 }
278 }
279
280 /// Returns the current timestamp according to this timer's hardware
281 /// [`Clock`], as an [`Instant`].
282 pub fn now(&self) -> Instant {
283 self.clock.now()
284 }
285
286 /// Borrows the hardware [`Clock`] definition used by this timer.
287 #[must_use]
288 pub fn clock(&self) -> &Clock {
289 &self.clock
290 }
291
292 /// Returns the maximum duration of [`Sleep`] futures driven by this timer.
293 pub fn max_duration(&self) -> Duration {
294 self.clock.max_duration()
295 }
296
297 /// Returns a [`Future`] that will complete in `duration`.
298 ///
299 /// # Returns
300 ///
301 /// The returned [`Sleep`] future will be driven by this timer, and will
302 /// complete once this timer has advanced by at least `duration`.
303 ///
304 /// # Panics
305 ///
306 /// This method panics if the provided duration exceeds the [maximum sleep
307 /// duration][max] allowed by this timer.
308 ///
309 /// For a version of this function that does not panic, see
310 /// [`Timer::try_sleep`].
311 ///
312 /// [global]: #global-timers
313 /// [max]: Timer::max_duration
314 /// [`Future`]: core::future::Future
315 #[track_caller]
316 pub fn sleep(&self, duration: Duration) -> Sleep<'_> {
317 expect_display(self.try_sleep(duration), "cannot create `Sleep` future")
318 }
319
320 /// Returns a [`Future`] that will complete in `duration`.
321 ///
322 /// # Returns
323 ///
324 /// - [`Ok`]`(`[`Sleep`]`)` if a new [`Sleep`] future was created
325 /// successfully.
326 /// - [`Err`]`(`[`TimerError::DurationTooLong`]`)` if the requested sleep
327 /// duration exceeds this timer's [maximum sleep
328 /// duration](Timer::max_duration`).
329 ///
330 /// The returned [`Sleep`] future will be driven by this timer, and will
331 /// complete once this timer has advanced by at least `duration`.
332 ///
333 /// # Panics
334 ///
335 /// This method does not panic. For a version of this method that panics
336 /// rather than returning an error, see [`Timer::sleep`].
337 ///
338 /// [`Future`]: core::future::Future
339 pub fn try_sleep(&self, duration: Duration) -> Result<Sleep<'_>, TimerError> {
340 let ticks = self.dur_to_ticks(duration)?;
341 Ok(self.sleep_ticks(ticks))
342 }
343
344 /// Returns a [`Future`] that will complete in `ticks` timer ticks.
345 ///
346 /// # Returns
347 ///
348 /// The returned [`Sleep`] future will be driven by this timer, and will
349 /// complete once this timer has advanced by at least `ticks` timer ticks.
350 ///
351 /// [`Future`]: core::future::Future
352 #[track_caller]
353 pub fn sleep_ticks(&self, ticks: Ticks) -> Sleep<'_> {
354 Sleep::new(self, ticks)
355 }
356
357 /// Attempt to turn the timer to the current `now` if the timer is not
358 /// locked, potentially waking any [`Sleep`] futures that have completed.
359 ///
360 /// # Returns
361 ///
362 /// - [`Some`]`(`[`Turn`]`)` if the lock was acquired and the wheel was
363 /// advanced. A [`Turn`] structure describes what occurred during this
364 /// turn of the wheel, including the [current time][elapsed] and the
365 /// [deadline of the next expiring timer][next], if one exists.
366 /// - [`None`] if the wheel was not advanced because the lock was already
367 /// held.
368 ///
369 /// [elapsed]: Turn::elapsed
370 /// [next]: Turn::time_to_next_deadline
371 ///
372 /// # Interrupt Safety
373 ///
374 /// This method will *never* spin if the timer wheel lock is held; instead,
375 /// it will add any new ticks to a counter of "pending" ticks and return
376 /// immediately. Therefore, it is safe to call this method in an interrupt
377 /// handler, as it will never acquire a lock that may already be locked.
378 ///
379 /// The [`Timer::turn`] method will spin to lock the timer wheel lock if
380 /// it is currently held, *ensuring* that any pending wakeups are processed.
381 /// That method should never be called in an interrupt handler.
382 ///
383 /// If a timer is driven primarily by calling `try_turn` in an interrupt
384 /// handler, it may be desirable to occasionally call [`Timer::turn`]
385 /// *outside* of an interrupt handler (i.e., as as part of an occasional
386 /// runtime bookkeeping process). This ensures that any pending ticks are
387 /// observed by the timer in a relatively timely manner.
388 #[inline]
389 pub fn try_turn(&self) -> Option<Turn> {
390 // `try_turn` may be called in an ISR, so it can never actually spin.
391 // instead, if the timer wheel is busy (e.g. the timer ISR was called on
392 // another core, or if a `Sleep` future is currently canceling itself),
393 // we just add to a counter of pending ticks, and bail.
394 self.core.try_with_lock(|core| self.advance_locked(core))
395 }
396
397 /// Advance the timer to the current time, ensuring any [`Sleep`] futures that
398 /// have completed are woken, even if a lock must be acquired.
399 ///
400 /// # Returns
401 ///
402 /// A [`Turn`] structure describing what occurred during this turn of the
403 /// wheel, including including the [current time][elapsed] and the [deadline
404 /// of the next expiring timer][next], if one exists.
405 ///
406 /// [elapsed]: Turn::elapsed
407 /// [next]: Turn::time_to_next_deadline
408 ///
409 /// # Interrupt Safety
410 ///
411 /// This method will spin to acquire the timer wheel lock if it is currently
412 /// held elsewhere. Therefore, this method must *NEVER* be called in an
413 /// interrupt handler!
414 ///
415 /// If a timer is advanced inside an interrupt handler, use the
416 /// [`Timer::try_turn`] method instead. If a timer is advanced primarily by
417 /// calls to [`Timer::try_turn`] in an interrupt handler, it may be
418 /// desirable to occasionally call `turn` outside an interrupt handler, to
419 /// ensure that pending ticks are drained frequently.
420 #[inline]
421 pub fn turn(&self) -> Turn {
422 self.core.with_lock(|core| self.advance_locked(core))
423 }
424
425 pub(in crate::time) fn ticks_to_dur(&self, ticks: Ticks) -> Duration {
426 clock::ticks_to_dur(self.clock.tick_duration(), ticks)
427 }
428
429 pub(in crate::time) fn dur_to_ticks(&self, duration: Duration) -> Result<Ticks, TimerError> {
430 clock::dur_to_ticks(self.clock.tick_duration(), duration)
431 }
432
433 fn advance_locked(&self, core: &mut wheel::Core) -> Turn {
434 // take any pending ticks.
435 let pending_ticks = self.pending_ticks.swap(0, AcqRel) as Ticks;
436 // we do two separate `advance` calls here instead of advancing once
437 // with the sum, because `ticks` + `pending_ticks` could overflow.
438 let mut expired: usize = 0;
439 if pending_ticks > 0 {
440 let (expiring, _next_deadline) = core.turn_to(pending_ticks);
441 expired = expired.saturating_add(expiring);
442 }
443
444 let mut now = self.clock.now_ticks();
445 loop {
446 let (expiring, next_deadline) = core.turn_to(now);
447 expired = expired.saturating_add(expiring);
448 if let Some(next) = next_deadline {
449 now = self.clock.now_ticks();
450 if now >= next.ticks {
451 // we've advanced past the next deadline, so we need to
452 // advance again.
453 continue;
454 }
455 }
456
457 return Turn {
458 expired,
459 next_deadline_ticks: next_deadline.map(|d| d.ticks),
460 now,
461 tick_duration: self.clock.tick_duration(),
462 };
463 }
464 }
465
466 #[cfg(all(test, not(loom)))]
467 fn reset(&self) {
468 self.core.with_lock(|core| {
469 *core = wheel::Core::new();
470 self.pending_ticks.store(0, Release);
471 });
472 }
473}
474
475impl fmt::Debug for Timer {
476 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
477 let Self {
478 clock,
479 pending_ticks,
480 core,
481 } = self;
482 let mut s = f.debug_struct("Timer");
483 s.field("clock", &clock)
484 .field("tick_duration", &clock.tick_duration())
485 .field("pending_ticks", &pending_ticks.load(Acquire));
486 core.try_with_lock(|core| s.field("core", &core).finish())
487 .unwrap_or_else(|| s.field("core", &"<locked>").finish())
488 }
489}
490
491// === impl Turn ===
492
493impl Turn {
494 /// Returns the number of ticks until the deadline at which the next
495 /// [`Sleep`] future expires, or [`None`] if no sleep futures are currently
496 /// scheduled.
497 #[inline]
498 #[must_use]
499 pub fn ticks_to_next_deadline(&self) -> Option<Ticks> {
500 self.next_deadline_ticks.map(|deadline| deadline - self.now)
501 }
502
503 /// Returns the [`Duration`] from the current time to the deadline of the
504 /// next [`Sleep`] future, or [`None`] if no sleep futures are currently
505 /// scheduled.
506 #[inline]
507 #[must_use]
508 pub fn time_to_next_deadline(&self) -> Option<Duration> {
509 self.ticks_to_next_deadline()
510 .map(|deadline| clock::ticks_to_dur(self.tick_duration, deadline))
511 }
512
513 /// Returns the total elapsed time since this timer wheel started running.
514 #[inline]
515 #[must_use]
516 pub fn elapsed(&self) -> Duration {
517 clock::ticks_to_dur(self.tick_duration, self.now)
518 }
519
520 /// Returns `true` if there are currently pending [`Sleep`] futures
521 /// scheduled in this timer wheel.
522 #[inline]
523 #[must_use]
524 pub fn has_remaining(&self) -> bool {
525 self.next_deadline_ticks.is_some()
526 }
527}
528
529// === impl TimerError ====
530
531impl fmt::Display for TimerError {
532 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
533 match self {
534 TimerError::NoGlobalTimer => f.pad(
535 "no global timer has been initialized! \
536 `set_global_timer` must be called before calling \
537 this function.",
538 ),
539 TimerError::DurationTooLong { requested, max } => write!(
540 f,
541 "requested duration {requested:?} exceeds this timer's \
542 maximum duration ({max:?}."
543 ),
544 }
545 }
546}
547
548feature! {
549 #![feature = "core-error"]
550 impl core::error::Error for TimerError {}
551}