bwk_backoff/
lib.rs

1use std::{
2    thread::{self, yield_now},
3    time::Duration,
4};
5
6use rand::{rng, Rng};
7
8pub struct Backoff {
9    current_step: u32,
10    max_sleep: Duration,
11}
12
13impl Backoff {
14    pub fn new_us(max_sleep: u64) -> Self {
15        let max_sleep = Duration::from_micros(max_sleep);
16        Self {
17            current_step: 0,
18            max_sleep,
19        }
20    }
21
22    pub fn new_ms(max_sleep: u64) -> Self {
23        let max_sleep = Duration::from_millis(max_sleep);
24        Self {
25            current_step: 0,
26            max_sleep,
27        }
28    }
29
30    /// Resets the backoff when work is done
31    pub fn reset(&mut self) {
32        self.current_step = 0;
33    }
34
35    /// Applies the next backoff strategy (yield or sleep)
36    pub fn snooze(&mut self) {
37        if self.current_step < 10 {
38            yield_now();
39        } else {
40            // exponential: base sleep = 2^(n - 10) * 10μs
41            let base = 1u64 << (self.current_step - 10).min(10);
42            let sleep_micros = base * 10;
43            let jitter: u64 = rng().random_range(0..sleep_micros / 2);
44            let total_sleep = Duration::from_micros(
45                (sleep_micros + jitter).min(self.max_sleep.as_micros() as u64),
46            );
47
48            thread::sleep(total_sleep);
49        }
50
51        self.current_step = self.current_step.saturating_add(1);
52    }
53}