1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
// Copyright (c) 2021-2024 Espresso Systems (espressosys.com)
// This file is part of the HotShot repository.
// You should have received a copy of the MIT License
// along with the HotShot repository. If not, see <https://mit-license.org/>.
use std::time::{Duration, Instant};
/// Track (with exponential backoff)
/// sending of some sort of message
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
pub struct ExponentialBackoff {
/// Value to reset to when reset is called
reset_val: Duration,
/// factor to back off by
backoff_factor: u32,
/// the current timeout amount
timeout: Duration,
/// when we started the timeout
started: Option<Instant>,
}
impl ExponentialBackoff {
/// Create new backoff
#[must_use]
pub fn new(backoff_factor: u32, next_timeout: Duration) -> Self {
ExponentialBackoff {
backoff_factor,
timeout: next_timeout * backoff_factor,
reset_val: next_timeout,
started: None,
}
}
/// reset backoff
pub fn reset(&mut self) {
self.timeout = self.reset_val;
}
/// start next timeout
/// result: whether or not we succeeded
/// if we succeeded, reset the timeout
/// else increment the timeout by a factor
/// of `timeout`
pub fn start_next(&mut self, result: bool) {
// success
if result {
self.timeout = self.reset_val;
self.started = Some(Instant::now());
}
// failure
else {
// note we want to prevent overflow.
if let Some(r) = self.timeout.checked_mul(self.backoff_factor) {
self.timeout = r;
}
self.started = Some(Instant::now());
}
}
/// Return the timeout duration and start the next timeout.
pub fn next_timeout(&mut self, result: bool) -> Duration {
let timeout = self.timeout;
self.start_next(result);
timeout
}
/// Whether or not the timeout is expired
#[must_use]
pub fn is_expired(&self) -> bool {
if let Some(then) = self.started {
then.elapsed() > self.timeout
} else {
true
}
}
/// Marked as expired regardless of time left.
pub fn expire(&mut self) {
self.started = None;
}
}
impl Default for ExponentialBackoff {
fn default() -> Self {
Self {
reset_val: Duration::from_millis(500),
backoff_factor: 2,
timeout: Duration::from_millis(500),
started: None,
}
}
}