use std::cell::RefCell;
use std::task::Waker;
use std::time::{Duration, Instant};
struct TimerEntry {
deadline: Instant,
waker: Waker,
}
struct TimerRegistry {
timers: Vec<TimerEntry>,
}
impl TimerRegistry {
const fn new() -> Self {
Self { timers: Vec::new() }
}
fn register(&mut self, deadline: Instant, waker: Waker) {
let pos = self.timers.partition_point(|e| e.deadline <= deadline);
self.timers.insert(pos, TimerEntry { deadline, waker });
}
fn fire_expired(&mut self) -> usize {
let now = Instant::now();
let split = self.timers.partition_point(|e| e.deadline <= now);
if split == 0 {
return 0;
}
let fired: Vec<_> = self.timers.drain(..split).collect();
let count = fired.len();
for entry in fired {
entry.waker.wake();
}
count
}
fn next_deadline(&self) -> Option<Duration> {
self.timers.first().map(|e| {
let now = Instant::now();
if e.deadline <= now {
Duration::ZERO
} else {
e.deadline - now
}
})
}
}
thread_local! {
static REGISTRY: RefCell<TimerRegistry> = const {
RefCell::new(TimerRegistry::new())
};
}
pub fn register(deadline: Instant, waker: Waker) {
REGISTRY.with(|r| r.borrow_mut().register(deadline, waker));
}
pub(crate) fn fire_expired() -> usize {
REGISTRY.with(|r| r.borrow_mut().fire_expired())
}
pub(crate) fn next_deadline() -> Option<Duration> {
REGISTRY.with(|r| r.borrow().next_deadline())
}