use std::cell::{Cell, RefCell};
use std::collections::BTreeMap;
use std::rc::Rc;
use std::sync::Arc;
use std::task::Poll;
use std::time::Instant;
use crate::metrics::Counters;
pub(crate) struct Heap {
queue: BTreeMap<(Instant, u64), Rc<Slot>>,
seq: u64,
metrics: Arc<Counters>,
}
struct Slot {
key: Cell<Option<(Instant, u64)>>,
elapsed: Cell<bool>,
waiters: RefCell<kio::WaiterList>,
}
impl Heap {
pub fn new(metrics: Arc<Counters>) -> Self {
Self {
queue: BTreeMap::new(),
seq: 0,
metrics,
}
}
pub fn fire(&mut self, now: Instant) -> bool {
let mut fired = false;
while let Some(entry) = self.queue.first_entry() {
if entry.key().0 > now {
break;
}
let slot = entry.remove();
slot.key.set(None);
slot.elapsed.set(true);
slot.waiters.borrow_mut().wake();
self.metrics.timers_fired.add(1);
fired = true;
}
fired
}
pub fn next(&self) -> Option<Instant> {
self.queue.first_key_value().map(|(key, _)| key.0)
}
fn insert(&mut self, at: Instant, slot: Rc<Slot>) -> (Instant, u64) {
self.seq += 1;
let key = (at, self.seq);
self.queue.insert(key, slot);
self.metrics.timers_armed.add(1);
key
}
fn cancel(&mut self, key: (Instant, u64)) {
if self.queue.remove(&key).is_some() {
self.metrics.timers_cancelled.add(1);
}
}
fn fire_one(&mut self, key: (Instant, u64)) {
if self.queue.remove(&key).is_some() {
self.metrics.timers_fired.add(1);
}
}
}
pub struct Timer {
at: Option<Instant>,
heap: Rc<RefCell<Heap>>,
slot: Rc<Slot>,
}
impl Timer {
pub(crate) fn from_heap(heap: Rc<RefCell<Heap>>) -> Self {
Self {
at: None,
heap,
slot: Rc::new(Slot {
key: Cell::new(None),
elapsed: Cell::new(false),
waiters: RefCell::new(kio::WaiterList::new()),
}),
}
}
}
impl Timer {
pub fn set(&mut self, at: Option<Instant>) {
if self.at == at {
return;
}
self.at = at;
let mut heap = self.heap.borrow_mut();
if let Some(key) = self.slot.key.take() {
heap.cancel(key);
}
self.slot.elapsed.set(false);
if let Some(at) = at {
let key = heap.insert(at, self.slot.clone());
self.slot.key.set(Some(key));
}
}
pub fn poll(&mut self, waiter: &kio::Waiter) -> Poll<()> {
if self.slot.elapsed.get() {
return Poll::Ready(());
}
let Some((at, _)) = self.slot.key.get() else {
return Poll::Pending;
};
if at <= Instant::now() {
self.heap.borrow_mut().fire_one(self.slot.key.take().expect("armed"));
self.slot.elapsed.set(true);
return Poll::Ready(());
}
waiter.register(&mut self.slot.waiters.borrow_mut());
Poll::Pending
}
}
impl Drop for Timer {
fn drop(&mut self) {
if let Some(key) = self.slot.key.take() {
self.heap.borrow_mut().cancel(key);
}
}
}
impl std::fmt::Debug for Timer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Timer")
.field("at", &self.slot.key.get().map(|key| key.0))
.field("elapsed", &self.slot.elapsed.get())
.finish()
}
}
impl Timer {
pub fn new(handle: &crate::Handle) -> Self {
handle.timer()
}
pub fn after(handle: &crate::Handle, duration: std::time::Duration) -> Self {
let mut timer = handle.timer();
timer.set(Instant::now().checked_add(duration));
timer
}
pub async fn wait(&mut self) {
kio::wait(|waiter| self.poll(waiter)).await
}
}