use std::time::Duration;
use crate::transaction::Reliability;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Timer {
A,
B,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
Trying100,
}
impl Timer {
pub const ALL: [Self; 13] = [
Self::A,
Self::B,
Self::D,
Self::E,
Self::F,
Self::G,
Self::H,
Self::I,
Self::J,
Self::K,
Self::L,
Self::M,
Self::Trying100,
];
}
#[derive(Debug, Clone, Copy)]
pub struct Timers {
pub t1: Duration,
pub t2: Duration,
pub t4: Duration,
}
impl Default for Timers {
fn default() -> Self {
Self {
t1: Duration::from_millis(500),
t2: Duration::from_secs(4),
t4: Duration::from_secs(5),
}
}
}
impl Timers {
#[must_use]
pub fn timeout(&self) -> Duration {
self.t1 * 64
}
#[must_use]
pub fn timer_d(&self, reliability: Reliability) -> Duration {
if reliability.is_reliable() {
Duration::ZERO
} else {
Duration::from_secs(32).max(self.timeout())
}
}
#[must_use]
pub fn absorb(&self, reliability: Reliability) -> Duration {
if reliability.is_reliable() {
Duration::ZERO
} else {
self.t4
}
}
#[must_use]
pub fn timer_j(&self, reliability: Reliability) -> Duration {
if reliability.is_reliable() {
Duration::ZERO
} else {
self.timeout()
}
}
#[must_use]
pub fn double(&self, current: Duration) -> Duration {
current.saturating_mul(2)
}
#[must_use]
pub fn double_capped(&self, current: Duration) -> Duration {
current.saturating_mul(2).min(self.t2)
}
#[must_use]
pub fn trying_100(&self) -> Duration {
Duration::from_millis(200)
}
}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::indexing_slicing
)]
mod tests {
use super::*;
#[test]
fn defaults_match_rfc3261_table_4() {
let t = Timers::default();
assert_eq!(t.t1, Duration::from_millis(500));
assert_eq!(t.t2, Duration::from_secs(4));
assert_eq!(t.t4, Duration::from_secs(5));
assert_eq!(t.timeout(), Duration::from_secs(32));
}
#[test]
fn backoff_doubles_and_timer_e_stops_at_t2() {
let t = Timers::default();
assert_eq!(t.double(t.t1), Duration::from_secs(1));
assert_eq!(t.double(Duration::from_secs(4)), Duration::from_secs(8));
assert_eq!(t.double_capped(Duration::from_secs(4)), t.t2);
assert_eq!(t.double_capped(Duration::from_secs(8)), t.t2);
}
#[test]
fn reliable_transports_collapse_the_absorption_timers() {
let t = Timers::default();
assert_eq!(t.absorb(Reliability::Reliable), Duration::ZERO);
assert_eq!(t.timer_j(Reliability::Reliable), Duration::ZERO);
assert_eq!(t.timer_d(Reliability::Reliable), Duration::ZERO);
assert_eq!(t.absorb(Reliability::Unreliable), t.t4);
assert_eq!(t.timer_j(Reliability::Unreliable), t.timeout());
assert_eq!(t.timer_d(Reliability::Unreliable), Duration::from_secs(32));
}
#[test]
fn timer_d_outlasts_the_transaction_even_with_a_large_t1() {
let t = Timers {
t1: Duration::from_secs(2),
..Timers::default()
};
assert_eq!(t.timeout(), Duration::from_secs(128));
assert_eq!(t.timer_d(Reliability::Unreliable), Duration::from_secs(128));
}
}