use std::cell::Cell;
use crate::entry::{EntryPtr, entry_ref, null_entry};
pub(crate) struct WheelSlot<T> {
entry_head: Cell<EntryPtr<T>>,
entry_tail: Cell<EntryPtr<T>>,
}
impl<T: 'static> WheelSlot<T> {
fn new() -> Self {
WheelSlot {
entry_head: Cell::new(null_entry()),
entry_tail: Cell::new(null_entry()),
}
}
#[inline]
pub(crate) fn is_empty(&self) -> bool {
self.entry_head.get().is_null()
}
#[inline]
pub(crate) unsafe fn push_entry(&self, entry_ptr: EntryPtr<T>) {
let entry = unsafe { entry_ref(entry_ptr) };
entry.set_prev(null_entry());
entry.set_next(null_entry());
let tail = self.entry_tail.get();
if tail.is_null() {
self.entry_head.set(entry_ptr);
} else {
let tail_entry = unsafe { entry_ref(tail) };
tail_entry.set_next(entry_ptr);
entry.set_prev(tail);
}
self.entry_tail.set(entry_ptr);
}
#[inline]
pub(crate) unsafe fn remove_entry(&self, entry_ptr: EntryPtr<T>) {
let entry = unsafe { entry_ref(entry_ptr) };
let prev = entry.prev();
let next = entry.next();
if prev.is_null() {
self.entry_head.set(next);
} else {
unsafe { entry_ref(prev) }.set_next(next);
}
if next.is_null() {
self.entry_tail.set(prev);
} else {
unsafe { entry_ref(next) }.set_prev(prev);
}
entry.set_prev(null_entry());
entry.set_next(null_entry());
}
#[inline]
pub(crate) fn entry_head(&self) -> EntryPtr<T> {
self.entry_head.get()
}
}
pub(crate) struct Level<T> {
slots: Box<[WheelSlot<T>]>,
active_slots: Cell<u64>,
shift: u32,
mask: usize,
range: u64,
}
impl<T: 'static> Level<T> {
pub(crate) fn new(slots_per_level: usize, level_index: usize, clk_shift: u32) -> Self {
let shift = (level_index as u32) * clk_shift;
let slots: Vec<WheelSlot<T>> = (0..slots_per_level).map(|_| WheelSlot::new()).collect();
Level {
slots: slots.into_boxed_slice(),
active_slots: Cell::new(0),
shift,
mask: slots_per_level - 1,
range: (slots_per_level as u64) << shift,
}
}
#[cfg(test)]
#[inline]
pub(crate) fn shift(&self) -> u32 {
self.shift
}
#[cfg(test)]
#[inline]
pub(crate) fn mask(&self) -> usize {
self.mask
}
#[inline]
pub(crate) fn range(&self) -> u64 {
self.range
}
#[inline]
pub(crate) fn slot_index(&self, deadline_ticks: u64) -> usize {
((deadline_ticks >> self.shift) as usize) & self.mask
}
#[inline]
pub(crate) fn slot(&self, index: usize) -> &WheelSlot<T> {
debug_assert!(index < self.slots.len());
&self.slots[index]
}
#[inline]
pub(crate) fn activate_slot(&self, slot_idx: usize) {
self.active_slots
.set(self.active_slots.get() | (1 << slot_idx));
}
#[inline]
pub(crate) fn deactivate_slot(&self, slot_idx: usize) {
self.active_slots
.set(self.active_slots.get() & !(1 << slot_idx));
}
#[inline]
pub(crate) fn active_slots(&self) -> u64 {
self.active_slots.get()
}
#[inline]
pub(crate) fn is_active(&self) -> bool {
self.active_slots.get() != 0
}
#[cfg(test)]
#[inline]
pub(crate) fn num_slots(&self) -> usize {
self.slots.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::entry::WheelEntry;
use nexus_slab::{Slot, unbounded};
fn make_entry<T>(
slab: &unbounded::Slab<WheelEntry<T>>,
deadline: u64,
value: T,
) -> EntryPtr<T> {
let entry = WheelEntry::new(deadline, value, 1);
let slot = slab.alloc(entry);
slot.into_raw()
}
#[test]
fn slot_push_remove_single() {
let slab = unsafe { unbounded::Slab::<WheelEntry<u64>>::with_chunk_capacity(16) };
let ws = WheelSlot::<u64>::new();
assert!(ws.is_empty());
let e1 = make_entry(&slab, 10, 100);
unsafe { ws.push_entry(e1) };
assert!(!ws.is_empty());
unsafe { ws.remove_entry(e1) };
assert!(ws.is_empty());
slab.free(unsafe { Slot::from_raw(e1) });
}
#[test]
fn slot_push_remove_multiple() {
let slab = unsafe { unbounded::Slab::<WheelEntry<u64>>::with_chunk_capacity(16) };
let ws = WheelSlot::<u64>::new();
let e1 = make_entry(&slab, 10, 1);
let e2 = make_entry(&slab, 20, 2);
let e3 = make_entry(&slab, 30, 3);
unsafe {
ws.push_entry(e1);
ws.push_entry(e2);
ws.push_entry(e3);
}
unsafe { ws.remove_entry(e2) };
assert_eq!(ws.entry_head(), e1);
unsafe {
assert_eq!(entry_ref(e1).next(), e3);
assert_eq!(entry_ref(e3).prev(), e1);
}
unsafe { ws.remove_entry(e1) };
assert_eq!(ws.entry_head(), e3);
unsafe { ws.remove_entry(e3) };
assert!(ws.is_empty());
unsafe {
slab.free(Slot::from_raw(e1));
slab.free(Slot::from_raw(e2));
slab.free(Slot::from_raw(e3));
}
}
#[test]
fn level_slot_index_computation() {
let lvl = Level::<u64>::new(64, 0, 3);
assert_eq!(lvl.shift(), 0);
assert_eq!(lvl.mask(), 63);
assert_eq!(lvl.num_slots(), 64);
assert_eq!(lvl.slot_index(0), 0);
assert_eq!(lvl.slot_index(1), 1);
assert_eq!(lvl.slot_index(63), 63);
assert_eq!(lvl.slot_index(64), 0); assert_eq!(lvl.range(), 64);
let lvl1 = Level::<u64>::new(64, 1, 3);
assert_eq!(lvl1.shift(), 3);
assert_eq!(lvl1.mask(), 63);
assert_eq!(lvl1.num_slots(), 64);
assert_eq!(lvl1.slot_index(0), 0);
assert_eq!(lvl1.slot_index(8), 1); assert_eq!(lvl1.slot_index(64), 8); assert_eq!(lvl1.range(), 512);
let lvl2 = Level::<u64>::new(64, 2, 3);
assert_eq!(lvl2.shift(), 6);
assert_eq!(lvl2.slot_index(64), 1); assert_eq!(lvl2.slot_index(512), 8); assert_eq!(lvl2.range(), 4096);
}
#[test]
fn level_active_slot_bitmask() {
let lvl = Level::<u64>::new(64, 0, 3);
assert!(!lvl.is_active());
assert_eq!(lvl.active_slots(), 0);
lvl.activate_slot(5);
assert!(lvl.is_active());
assert_eq!(lvl.active_slots(), 1 << 5);
lvl.activate_slot(10);
assert_eq!(lvl.active_slots(), (1 << 5) | (1 << 10));
lvl.activate_slot(5);
assert_eq!(lvl.active_slots(), (1 << 5) | (1 << 10));
lvl.deactivate_slot(5);
assert_eq!(lvl.active_slots(), 1 << 10);
lvl.deactivate_slot(10);
assert!(!lvl.is_active());
}
}