use std::time::Instant;
use strum::EnumCount;
use strum_macros::EnumCount;
use strum_macros::EnumIter;
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, EnumIter, EnumCount)]
pub(crate) enum Timer {
LossDetection,
Ack,
Pacer,
Idle,
Handshake,
Draining,
KeyDiscard,
KeepAlive,
PathChallenge,
}
#[derive(Debug, Copy, Clone, Default)]
pub(crate) struct TimerTable {
expires: [Option<Instant>; Timer::COUNT],
}
impl TimerTable {
pub fn set(&mut self, timer: Timer, time: Instant) {
self.expires[timer as usize] = Some(time);
}
pub fn get(&self, timer: Timer) -> Option<Instant> {
self.expires[timer as usize]
}
pub fn stop(&mut self, timer: Timer) {
self.expires[timer as usize] = None;
}
pub fn next_timeout(&self) -> Option<Instant> {
self.expires.iter().filter_map(|&x| x).min()
}
pub fn is_expired(&self, timer: Timer, after: Instant) -> bool {
self.expires[timer as usize].is_some_and(|x| x <= after)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::ops::Add;
use std::time::Duration;
use std::time::Instant;
#[test]
fn timer_operation() {
let mut timers = TimerTable::default();
assert_eq!(timers.next_timeout(), None);
let now = Instant::now();
let loss_time = now.add(Duration::from_millis(200));
let idle_time = now.add(Duration::from_millis(3000));
timers.set(Timer::LossDetection, loss_time);
timers.set(Timer::Idle, idle_time);
assert_eq!(timers.get(Timer::LossDetection), Some(loss_time));
assert_eq!(timers.get(Timer::Idle), Some(idle_time));
assert_eq!(timers.get(Timer::Draining), None);
assert_eq!(timers.get(Timer::KeyDiscard), None);
assert_eq!(timers.next_timeout(), Some(loss_time));
timers.stop(Timer::LossDetection);
assert_eq!(timers.get(Timer::LossDetection), None);
assert_eq!(timers.get(Timer::Idle), Some(idle_time));
assert_eq!(timers.next_timeout(), Some(idle_time));
}
#[test]
fn timer_expiration() {
let mut timers = TimerTable::default();
let now = Instant::now();
let loss_time = now.add(Duration::from_millis(200));
let idle_time = now.add(Duration::from_millis(3000));
timers.set(Timer::LossDetection, loss_time);
timers.set(Timer::Idle, idle_time);
assert_eq!(timers.is_expired(Timer::LossDetection, now), false);
assert_eq!(timers.is_expired(Timer::Idle, now), false);
let now = loss_time;
assert_eq!(timers.is_expired(Timer::LossDetection, now), true);
assert_eq!(timers.is_expired(Timer::Idle, now), false);
let now = idle_time;
assert_eq!(timers.is_expired(Timer::LossDetection, now), true);
assert_eq!(timers.is_expired(Timer::Idle, now), true);
}
}