use crate::TimerError;
const LEVELS: usize = 3;
const SLOTS: usize = 64;
const MASK: usize = SLOTS - 1;
const LEVEL_SHIFT: [u32; LEVELS] = [0, 6, 12];
const LEVEL_RANGE: [usize; LEVELS] = [
SLOTS, SLOTS * SLOTS, SLOTS * SLOTS * SLOTS, ];
struct Entry<V> {
id: u64,
deadline: u64,
value: Option<V>,
cancelled: bool,
}
pub struct HierarchicalTimerWheel<V> {
wheels: [[Vec<Entry<V>>; SLOTS]; LEVELS],
now: u64,
next_id: u64,
cascades: u64,
pending: usize,
}
impl<V> HierarchicalTimerWheel<V> {
pub fn new() -> Self {
let wheels = std::array::from_fn(|_| std::array::from_fn(|_| Vec::new()));
Self {
wheels,
now: 0,
next_id: 1,
cascades: 0,
pending: 0,
}
}
pub fn now(&self) -> u64 {
self.now
}
pub fn cascades(&self) -> u64 {
self.cascades
}
pub fn pending(&self) -> usize {
self.pending
}
pub fn is_empty(&self) -> bool {
self.pending == 0
}
pub const fn max_delay() -> usize {
LEVEL_RANGE[LEVELS - 1]
}
pub fn schedule(&mut self, delay: u64, value: V) -> u64 {
let cap = Self::max_delay() as u64;
let d = delay.min(cap.saturating_sub(1));
self.try_schedule(d, value).expect("clamped delay fits")
}
pub fn try_schedule(&mut self, delay: u64, value: V) -> Result<u64, TimerError> {
let max = Self::max_delay() as u64;
if delay >= max {
return Err(TimerError::DelayTooLong { delay, max });
}
let id = self.next_id;
self.next_id += 1;
self.insert(id, self.now + delay, value);
Ok(id)
}
pub fn cancel(&mut self, id: u64) -> bool {
for lvl in 0..LEVELS {
for slot in 0..SLOTS {
for e in &mut self.wheels[lvl][slot] {
if e.id == id && !e.cancelled {
e.cancelled = true;
e.value = None;
self.pending -= 1;
return true;
}
}
}
}
false
}
pub fn reschedule(&mut self, id: u64, delay: u64) -> bool {
let cap = Self::max_delay() as u64;
let d = delay.min(cap.saturating_sub(1));
for lvl in 0..LEVELS {
for slot in 0..SLOTS {
let Some(pos) = self.wheels[lvl][slot]
.iter()
.position(|e| e.id == id && !e.cancelled)
else {
continue;
};
let entry = self.wheels[lvl][slot].swap_remove(pos);
let Some(value) = entry.value else {
return false;
};
self.pending -= 1;
self.insert(id, self.now + d, value);
return true;
}
}
false
}
pub fn drain(&mut self) -> Vec<V> {
let mut out = Vec::with_capacity(self.pending);
for lvl in 0..LEVELS {
for slot in 0..SLOTS {
for mut e in std::mem::take(&mut self.wheels[lvl][slot]) {
if e.cancelled {
continue;
}
if let Some(v) = e.value.take() {
out.push(v);
}
}
}
}
self.pending = 0;
out
}
pub fn clear(&mut self) {
for lvl in 0..LEVELS {
for slot in 0..SLOTS {
self.wheels[lvl][slot].clear();
}
}
self.pending = 0;
self.now = 0;
}
fn insert(&mut self, id: u64, deadline: u64, value: V) {
let entry = Entry {
id,
deadline,
value: Some(value),
cancelled: false,
};
let (lvl, slot) = self.bucket_for(deadline);
self.wheels[lvl][slot].push(entry);
self.pending += 1;
}
pub fn tick(&mut self) -> Vec<V> {
self.now += 1;
for lvl in (1..LEVELS).rev() {
let lower_period = 1u64 << LEVEL_SHIFT[lvl];
if self.now % lower_period == 0 {
let slot = ((self.now >> LEVEL_SHIFT[lvl]) as usize) & MASK;
let entries = std::mem::take(&mut self.wheels[lvl][slot]);
for e in entries {
if e.cancelled {
continue;
}
self.cascades += 1;
let (new_lvl, new_slot) = self.bucket_for(e.deadline);
self.wheels[new_lvl][new_slot].push(e);
}
}
}
let slot = (self.now as usize) & MASK;
let entries = std::mem::take(&mut self.wheels[0][slot]);
let mut fired = Vec::new();
for mut e in entries {
if e.cancelled {
continue;
}
if e.deadline != self.now {
let (lvl, slot) = self.bucket_for(e.deadline);
self.wheels[lvl][slot].push(e);
continue;
}
self.pending -= 1;
if let Some(v) = e.value.take() {
fired.push(v);
}
}
fired
}
fn bucket_for(&self, deadline: u64) -> (usize, usize) {
let diff = deadline.saturating_sub(self.now);
let lvl = if diff < LEVEL_RANGE[0] as u64 {
0
} else if diff < LEVEL_RANGE[1] as u64 {
1
} else {
2
};
let slot = ((deadline >> LEVEL_SHIFT[lvl]) as usize) & MASK;
(lvl, slot)
}
}
impl<V> Default for HierarchicalTimerWheel<V> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
#[path = "hierarchical_tests.rs"]
mod tests;