use crate::live::session::{Link, Session};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Timing {
pub ping_after_ms: u64,
pub silent_ms: u64,
pub steady_ms: u64,
pub first_retry_ms: u64,
pub last_retry_ms: u64,
}
impl Timing {
pub const DEFAULT: Timing = Timing {
ping_after_ms: 30_000,
silent_ms: 60_000,
steady_ms: 30_000,
first_retry_ms: 250,
last_retry_ms: 20_000,
};
}
impl Default for Timing {
fn default() -> Self {
Timing::DEFAULT
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Tick {
Wait,
Ping,
Reconnect,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Retry {
pub after_ms: u64,
pub unanswered: Vec<u64>,
}
impl Session {
pub fn tick(&mut self, now: u64) -> Tick {
let Link::Open { heard, kept, pinged, .. } = &mut self.link else { return Tick::Wait };
if now.saturating_sub(*heard) >= self.timing.silent_ms {
return Tick::Reconnect;
}
if now.saturating_sub((*kept).max(*pinged)) >= self.timing.ping_after_ms {
*pinged = now;
return Tick::Ping;
}
Tick::Wait
}
#[must_use]
pub fn due(&self) -> Option<u64> {
let Link::Open { heard, kept, pinged, .. } = self.link else { return None };
let ping = kept.max(pinged).saturating_add(self.timing.ping_after_ms);
let silent = heard.saturating_add(self.timing.silent_ms);
Some(ping.min(silent))
}
pub fn closed(&mut self, now: u64, random: u64) -> Retry {
if let Link::Open { since, talked: true, .. } = self.link
&& now.saturating_sub(since) >= self.timing.steady_ms
{
self.failures = 0;
}
self.link = Link::Closed;
let doubled = 1u64.checked_shl(self.failures).unwrap_or(u64::MAX);
let step = self.timing.first_retry_ms.saturating_mul(doubled).min(self.timing.last_retry_ms);
let jitter = random % (step / 4).saturating_add(1);
self.failures = self.failures.saturating_add(1);
Retry { after_ms: step.saturating_add(jitter), unanswered: std::mem::take(&mut self.waiting) }
}
}
#[cfg(test)]
#[path = "tests/timing.rs"]
mod tests;