use std::collections::BTreeMap;
use std::mem;
use std::task::Waker;
use std::time::{Duration, Instant};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct TimerKey {
tick: Instant,
discriminator: u32,
}
impl TimerKey {
const fn new(tick: Instant, id: u32) -> Self {
Self { tick, discriminator: id }
}
pub const fn tick(&self) -> Instant {
self.tick
}
}
pub(crate) const TIMER_RESOLUTION: Duration = Duration::from_millis(1);
#[derive(Debug, Default)]
pub(crate) struct Timers {
wakers: BTreeMap<TimerKey, Waker>,
last_discriminator: u32,
alive: bool,
}
impl Timers {
pub fn len(&self) -> usize {
self.wakers.len()
}
pub fn alive(&self) -> bool {
self.alive
}
#[cfg(test)]
fn contains(&self, id: TimerKey) -> bool {
self.wakers.contains_key(&id)
}
pub fn register(&mut self, when: Instant, waker: Waker) -> TimerKey {
self.last_discriminator = self.last_discriminator.wrapping_add(1);
let key = TimerKey::new(when, self.last_discriminator);
self.wakers.insert(key, waker);
key
}
pub fn unregister(&mut self, id: TimerKey) {
self.wakers.remove(&id);
}
pub fn next_timer(&self) -> Option<Instant> {
self.wakers.keys().next().map(TimerKey::tick)
}
#[cfg_attr(test, mutants::skip)] pub fn advance_timers(&mut self, now: Instant) -> Option<Instant> {
self.alive = true;
let adjusted_now = now.checked_add(Duration::from_nanos(1)).unwrap_or(now);
match self.wakers.first_entry() {
Some(entry) => {
if entry.key().tick() <= adjusted_now {
let pending = self.wakers.split_off(&TimerKey::new(adjusted_now, 0));
let ready = mem::replace(&mut self.wakers, pending);
for (_, waker) in ready {
waker.wake();
}
return self.next_timer();
}
Some(entry.key().tick())
}
None => None,
}
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
use crate::Clock;
use crate::state::ClockState;
#[test]
fn two_timers_same_instant() {
let mut timers = Timers::default();
let anchor = Instant::now();
let when = anchor + Duration::from_secs(2);
let key1 = timers.register(when, Waker::noop().clone());
let key2 = timers.register(when, Waker::noop().clone());
assert_ne!(key1, key2);
timers.advance_timers(when + Duration::from_secs(1));
assert_eq!(timers.len(), 0);
}
#[test]
fn advance_timers_ensure_order() {
let mut timers = Timers::default();
let anchor = Instant::now();
let timer_first = anchor + Duration::from_secs(1);
let timer_second = anchor + Duration::from_secs(2);
let id1 = timers.register(timer_first, Waker::noop().clone());
let _id2 = timers.register(timer_second, Waker::noop().clone());
assert_eq!(timers.len(), 2);
timers.advance_timers(timer_first + Duration::from_nanos(1));
assert_eq!(timers.len(), 1);
assert!(!timers.contains(id1));
timers.advance_timers(timer_second + Duration::from_nanos(1));
assert_eq!(timers.len(), 0);
}
#[test]
fn timer_resolution_ensure_correct_value() {
assert_eq!(TIMER_RESOLUTION, Duration::from_millis(1));
}
#[test]
fn register_timer_with_clock() {
let clock = Clock::new_system_frozen();
let id = clock.register_timer(Instant::now(), Waker::noop().clone());
match clock.clock_state() {
ClockState::ClockControl(_) => panic!("we are using the system clock"),
ClockState::System(timers) => assert!(timers.with_timers(|t| t.contains(id))),
}
}
#[test]
fn unregister_timer_with_clock() {
let clock = Clock::new_system_frozen();
let id = clock.register_timer(Instant::now(), Waker::noop().clone());
clock.unregister_timer(id);
assert_eq!(clock.clock_state().timers_len(), 0);
}
#[test]
fn unregister_ok() {
let mut timers = Timers::default();
let id = timers.register(Instant::now(), Waker::noop().clone());
assert!(timers.contains(id));
timers.unregister(id);
assert!(!timers.contains(id));
}
#[test]
fn next_timer_ok() {
let mut timers = Timers::default();
let now = Instant::now();
let _ = timers.register(now, Waker::noop().clone());
let _ = timers.register(now.checked_add(Duration::from_secs(1)).unwrap(), Waker::noop().clone());
assert_eq!(timers.next_timer(), Some(now));
}
#[test]
fn advance_timers_ensure_correct_result() {
let mut timers = Timers::default();
let now = Instant::now();
assert!(timers.advance_timers(now).is_none());
let next = now.checked_add(Duration::from_secs(1)).unwrap();
let _ = timers.register(next, Waker::noop().clone());
assert_eq!(timers.advance_timers(now), Some(next));
assert_eq!(timers.advance_timers(next), None);
}
}