//! Exponential backoff: a delay that doubles, up to a limit, while something keeps //! failing, and starts over when it succeeds. Only the delay; each user keeps its own //! point in time (next attempt, deadline). use std::time::Duration; #[derive(Clone, Copy, Debug)] pub(crate) struct Backoff { initial: Duration, max: Duration, current: Duration, } impl Backoff { pub(crate) const fn new(initial: Duration, max: Duration) -> Self { Self { initial, max, current: initial, } } /// The delay to use now. pub(crate) fn current(&self) -> Duration { self.current } /// Doubles the delay, up to the limit. pub(crate) fn grow(&mut self) { self.current = self.current.saturating_mul(2).min(self.max); } /// The delay to use now; the next call gives twice as much (up to the limit). pub(crate) fn next(&mut self) -> Duration { let delay = self.current; self.grow(); delay } /// Back to the initial delay. pub(crate) fn reset(&mut self) { self.current = self.initial; } } #[cfg(test)] mod tests { use super::*; #[test] fn doubles_up_to_the_limit_and_resets() { let mut backoff = Backoff::new(Duration::from_secs(1), Duration::from_secs(5)); let delays: Vec<_> = (0..5).map(|_| backoff.next().as_secs()).collect(); assert_eq!(delays, [1, 2, 4, 5, 5]); backoff.reset(); assert_eq!(backoff.current(), Duration::from_secs(1)); } }