use core::task::Waker;
const SLOT_BITS: u32 = 6;
const SLOTS: usize = 1 << SLOT_BITS;
const LEVELS: usize = 6;
const SPAN: u64 = 1 << (SLOT_BITS as u64 * LEVELS as u64);
#[cfg(all(test, any(target_os = "linux", target_os = "android", precise_timer)))]
pub(crate) const TOP_SLOT: u64 = SPAN / SLOTS as u64;
const NIL: usize = usize::MAX;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct TimerKey {
index: usize,
generation: u64,
}
impl TimerKey {
#[cfg(test)]
const FIRED: Self = Self {
index: usize::MAX,
generation: 0,
};
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Place {
Free,
Armed { level: u8, slot: u8 },
}
struct Entry {
when: u64,
waker: Option<Waker>,
prev: usize,
next: usize,
generation: u64,
place: Place,
}
struct Level {
occupied: u64,
heads: [usize; SLOTS],
}
impl Level {
const fn new() -> Self {
Self {
occupied: 0,
heads: [NIL; SLOTS],
}
}
}
pub(crate) struct Wheel {
elapsed: u64,
levels: [Level; LEVELS],
entries: Vec<Entry>,
free: usize,
armed: usize,
phantom: usize,
tag: u64,
}
impl Default for Wheel {
fn default() -> Self {
Self::new()
}
}
#[inline]
fn level_for(elapsed: u64, when: u64) -> usize {
let masked = ((elapsed ^ when) | (SLOTS as u64 - 1)).min(SPAN - 1);
let significant = 63 - masked.leading_zeros();
(significant / SLOT_BITS) as usize
}
#[inline]
fn slot_for(when: u64, level: usize) -> usize {
((when >> (level as u32 * SLOT_BITS)) as usize) & (SLOTS - 1)
}
#[cfg(test)]
fn level_by_windows(elapsed: u64, when: u64) -> usize {
(0..LEVELS)
.find(|&l| {
let width = SLOT_BITS * (l as u32 + 1);
elapsed >> width == when >> width
})
.unwrap_or(LEVELS - 1)
}
impl Wheel {
pub(crate) const fn new() -> Self {
Self {
elapsed: 0,
levels: [const { Level::new() }; LEVELS],
entries: Vec::new(),
free: NIL,
armed: 0,
phantom: 0,
tag: 0,
}
}
pub(crate) const fn tagged(tag: u64) -> Self {
let mut wheel = Self::new();
wheel.tag = tag;
wheel
}
pub(crate) fn set_phantom(&mut self, n: usize) {
self.armed = self.armed - self.phantom + n;
self.phantom = n;
}
#[cfg(test)]
pub(crate) fn elapsed(&self) -> u64 {
self.elapsed
}
pub(crate) fn armed(&self) -> usize {
self.armed
}
pub(crate) fn take_wakers(&mut self) -> Vec<Waker> {
self.entries
.iter_mut()
.filter_map(|entry| entry.waker.take())
.collect()
}
#[inline(always)]
pub(crate) fn insert(&mut self, when: u64, waker: Waker) -> Result<TimerKey, Waker> {
if when <= self.elapsed {
return Err(waker);
}
let index = self.alloc();
let entry = &mut self.entries[index];
entry.when = when;
entry.waker = Some(waker);
self.link(index);
Ok(TimerKey {
index,
generation: self.entries[index].generation,
})
}
pub(crate) fn is_fired(&self, key: TimerKey) -> bool {
self.entry(key).is_none()
}
pub(crate) fn wakes(&self, key: TimerKey, waker: &Waker) -> bool {
self.entry(key)
.is_some_and(|e| e.waker.as_ref().is_some_and(|old| old.will_wake(waker)))
}
pub(crate) fn replace_waker(&mut self, key: TimerKey, waker: Waker) -> Option<Waker> {
match self.live(key) {
Some(index) => self.entries[index].waker.replace(waker),
None => Some(waker),
}
}
#[cfg(test)]
pub(crate) fn reset(&mut self, key: TimerKey, when: u64, waker: &Waker) {
let Some(index) = self.live(key) else { return };
self.unlink(index);
if when <= self.elapsed {
drop(self.release(index));
return;
}
let entry = &mut self.entries[index];
entry.when = when;
if !entry.waker.as_ref().is_some_and(|w| w.will_wake(waker)) {
entry.waker = Some(waker.clone());
}
self.link(index);
}
pub(crate) fn remove(&mut self, key: TimerKey) -> Option<Waker> {
let index = self.live(key)?;
self.unlink(index);
self.release(index)
}
#[inline]
fn release(&mut self, index: usize) -> Option<Waker> {
let entry = &mut self.entries[index];
let waker = entry.waker.take();
entry.place = Place::Free;
entry.generation = entry.generation.wrapping_add(1);
entry.next = self.free;
self.free = index;
waker
}
pub(crate) fn next_deadline(&self) -> Option<u64> {
self.next_slot().map(|(_, _, deadline)| deadline)
}
pub(crate) fn advance(&mut self, now: u64, fired: &mut Vec<Waker>) {
while let Some((level, slot, deadline)) = self.next_slot() {
if deadline > now {
break;
}
self.elapsed = deadline;
let mut index = self.levels[level].heads[slot];
self.levels[level].heads[slot] = NIL;
self.levels[level].occupied &= !(1 << slot);
while index != NIL {
let entry = &mut self.entries[index];
let next = entry.next;
if entry.when <= now {
self.armed -= 1;
if let Some(waker) = entry.waker.take() {
fired.push(waker);
}
self.release(index);
} else {
self.armed -= 1;
self.link(index);
}
index = next;
}
}
self.elapsed = self.elapsed.max(now);
}
fn next_slot(&self) -> Option<(usize, usize, u64)> {
let mut best: Option<(usize, usize, u64)> = None;
for (level, lv) in self.levels.iter().enumerate() {
if lv.occupied == 0 {
continue;
}
let shift = level as u32 * SLOT_BITS;
let now_slot = slot_for(self.elapsed, level);
let range = 1u64 << (shift + SLOT_BITS);
let window = self.elapsed & !(range - 1);
let after = if now_slot + 1 < SLOTS {
lv.occupied & (u64::MAX << (now_slot + 1))
} else {
0
};
let (slot, deadline) = if after != 0 {
let slot = after.trailing_zeros() as usize;
(slot, window + ((slot as u64) << shift))
} else {
debug_assert_eq!(
level,
LEVELS - 1,
"a timer behind the wheel on level {level}"
);
let slot = lv.occupied.trailing_zeros() as usize;
(slot, window + range + ((slot as u64) << shift))
};
if best.is_none_or(|(_, _, d)| deadline < d) {
best = Some((level, slot, deadline));
}
}
best
}
#[inline(always)]
fn link(&mut self, index: usize) {
let when = self.entries[index].when;
let placed = when.min(self.elapsed + SPAN - 1);
let level = level_for(self.elapsed, placed);
let slot = slot_for(placed, level);
let lv = &mut self.levels[level];
let head = lv.heads[slot];
lv.heads[slot] = index;
lv.occupied |= 1 << slot;
if head != NIL {
self.entries[head].prev = index;
}
let entry = &mut self.entries[index];
entry.prev = NIL;
entry.next = head;
entry.place = Place::Armed {
level: level as u8,
slot: slot as u8,
};
self.armed += 1;
}
fn unlink(&mut self, index: usize) {
let (prev, next, level, slot) = {
let entry = &self.entries[index];
let Place::Armed { level, slot } = entry.place else {
return;
};
(entry.prev, entry.next, level as usize, slot as usize)
};
if prev == NIL {
self.levels[level].heads[slot] = next;
if next == NIL {
self.levels[level].occupied &= !(1 << slot);
}
} else {
self.entries[prev].next = next;
}
if next != NIL {
self.entries[next].prev = prev;
}
self.armed -= 1;
}
#[inline]
fn alloc(&mut self) -> usize {
if self.free != NIL {
let index = self.free;
self.free = self.entries[index].next;
return index;
}
self.grow()
}
#[cold]
#[inline(never)]
fn grow(&mut self) -> usize {
let index = self.entries.len();
self.entries.push(Entry {
when: 0,
waker: None,
prev: NIL,
next: NIL,
generation: self.tag,
place: Place::Free,
});
index
}
#[cfg(test)]
fn age(&mut self, key: TimerKey, releases: u64) {
let entry = &mut self.entries[key.index];
assert_eq!(entry.place, Place::Free, "only a free entry ages");
entry.generation = entry.generation.wrapping_add(releases as _);
}
fn entry(&self, key: TimerKey) -> Option<&Entry> {
self.entries
.get(key.index)
.filter(|e| e.generation == key.generation && e.place != Place::Free)
}
fn live(&self, key: TimerKey) -> Option<usize> {
self.entry(key).map(|_| key.index)
}
}
#[cfg(test)]
mod tests;