Next Generation WASM Microkernel Operating System
wasm
os
rust
microkernel
1// Copyright 2025 Jonas Kruckenberg
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use core::hint;
9
10/// An [exponential backoff] for spin loops.
11///
12/// This is a helper struct for spinning in a busy loop, with an exponentially
13/// increasing number of spins up to a maximum value.
14///
15/// [exponential backoff]: https://en.wikipedia.org/wiki/Exponential_backoff
16#[derive(Debug, Copy, Clone)]
17pub struct Backoff {
18 exp: u8,
19 max: u8,
20}
21
22// === impl Backoff ===
23
24impl Backoff {
25 /// The default maximum exponent (2^8).
26 ///
27 /// This is the maximum exponent returned by [`Backoff::new()`] and
28 /// [`Backoff::default()`]. To override the maximum exponent, use
29 /// [`Backoff::with_max_exponent()`].
30 pub const DEFAULT_MAX_EXPONENT: u8 = 8;
31
32 /// Returns a new exponential backoff with the maximum exponent set to
33 /// [`Self::DEFAULT_MAX_EXPONENT`].
34 #[must_use]
35 pub const fn new() -> Self {
36 Self {
37 exp: 0,
38 max: Self::DEFAULT_MAX_EXPONENT,
39 }
40 }
41
42 /// Returns a new exponential backoff with the provided max exponent.
43 ///
44 /// # Panics
45 ///
46 /// Panics if the `max` exponent is larger than [`Self::DEFAULT_MAX_EXPONENT`].
47 #[must_use]
48 pub fn with_max_exponent(max: u8) -> Self {
49 assert!(max <= Self::DEFAULT_MAX_EXPONENT);
50 Self { exp: 0, max }
51 }
52
53 /// Backs off in a spin loop.
54 ///
55 /// This should be used when an operation needs to be retried because
56 /// another thread or core made progress. Depending on the target
57 /// architecture, this will generally issue a sequence of `pause`
58 /// instructions.
59 ///
60 /// Each time this function is called, it will issue `2^exp` [spin loop
61 /// hints], where `exp` is the current exponent value (starting at 0). If
62 /// `exp` is less than the configured maximum exponent, the exponent is
63 /// incremented once the spin is complete.
64 ///
65 /// [spin loop hints]: hint::spin_loop
66 #[inline(always)]
67 pub fn spin(&mut self) {
68 // Issue 2^exp pause instructions.
69 let spins = 1_u32 << self.exp;
70
71 for _ in 0..spins {
72 // In tests, especially in loom tests, we need to yield the thread back to the runtime
73 // so it can make progress. See https://github.com/tokio-rs/loom/issues/162#issuecomment-665128979
74 #[cfg(loom)]
75 crate::loom::thread::yield_now();
76
77 hint::spin_loop();
78 }
79
80 if self.exp < self.max {
81 self.exp += 1;
82 }
83 }
84
85 #[inline(always)]
86 pub fn reset(&mut self) {
87 self.exp = 0;
88 }
89}
90
91impl Default for Backoff {
92 fn default() -> Self {
93 Self::new()
94 }
95}