use std::num::NonZeroUsize;
use std::{cell::Cell, cmp, fmt, rc::Rc, rc::Weak};
use crate::HashMap;
use crate::time::Millis;
use super::wheel::{TIMER, TimerEntry};
pub struct CallbackTimer {
pub(super) hnd: NonZeroUsize,
pub(super) state: Rc<CallbackState>,
}
impl CallbackTimer {
pub fn new(duration: Millis, callback: impl Fn() + 'static) -> Self {
let state = Rc::new(CallbackState {
callback: Box::new(callback),
arm: Cell::new(0),
pending: Cell::new(true),
});
TIMER.with(|timer| {
let (idx, expiry) = timer.calc_bucket(u64::from(cmp::max(duration.0, 1)));
let no = timer.with_wheel(|wheel| {
let no = wheel.timers.insert(TimerEntry {
bucket: None,
bucket_entry: 0,
callback: true,
task: None,
});
wheel
.callbacks
.get_or_insert_with(|| Box::new(Callbacks::default()))
.entries
.insert(
no,
CallbackTicket {
state: Rc::downgrade(&state),
arm: 0,
generation: timer.generation.get(),
},
);
wheel.link(no, idx);
no
});
let handle = Self {
hnd: NonZeroUsize::new(no).unwrap(),
state,
};
timer.update_next_expiry(expiry);
handle
})
}
pub fn reset(&self, duration: Millis) {
TIMER.with(|timer| {
let (idx, expiry) = timer.calc_bucket(u64::from(cmp::max(duration.0, 1)));
timer.with_wheel(|wheel| {
let ticket = wheel
.callbacks
.as_mut()
.unwrap()
.entries
.get_mut(&self.hnd.get())
.unwrap();
ticket.arm = ticket.arm.wrapping_add(1);
ticket.generation = timer.generation.get();
self.state.arm.set(ticket.arm);
self.state.pending.set(true);
wheel.relink(self.hnd.get(), idx);
});
timer.update_next_expiry(expiry);
});
}
pub fn cancel(&self) {
self.state.pending.set(false);
TIMER.with(|timer| timer.with_wheel(|wheel| wheel.unlink(self.hnd.get())));
}
pub fn is_elapsed(&self) -> bool {
TIMER.with(|timer| timer.with_wheel(|wheel| wheel.timers[self.hnd.get()].bucket.is_none()))
}
}
impl fmt::Debug for CallbackTimer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CallbackTimer")
.field("handle", &self.hnd)
.finish_non_exhaustive()
}
}
impl Drop for CallbackTimer {
fn drop(&mut self) {
self.state.pending.set(false);
let _ = TIMER.try_with(|timer| {
timer.with_wheel(|wheel| {
wheel.unlink(self.hnd.get());
wheel.timers.remove(self.hnd.get());
wheel
.callbacks
.as_mut()
.unwrap()
.entries
.remove(&self.hnd.get());
});
});
}
}
pub(super) struct CallbackState {
pub(super) callback: Box<dyn Fn()>,
pub(super) arm: Cell<u64>,
pub(super) pending: Cell<bool>,
}
#[derive(Clone)]
pub(super) struct CallbackTicket {
pub(super) state: Weak<CallbackState>,
pub(super) arm: u64,
pub(super) generation: u64,
}
#[derive(Default)]
pub(super) struct Callbacks {
pub(super) entries: HashMap<usize, CallbackTicket>,
pub(super) ready: Vec<CallbackTicket>,
}