use std::fmt;
use std::future::{poll_fn, Future};
use std::pin::{pin, Pin};
use std::task::{Context, Poll, Waker};
use std::time::Duration;
pub use std::time::Instant;
use crate::runtime;
use crate::slab::Slab;
const LEVELS: usize = 6;
const SLOTS: usize = 64;
const SLOT_MASK: u64 = (SLOTS as u64) - 1;
const MAX_TICKS: u64 = 1 << (6 * LEVELS);
const NIL: usize = usize::MAX;
const NANOS_PER_TICK: u128 = 1_000_000;
pub(crate) type TimerKey = (usize, u64);
struct Entry {
when: u64,
uid: u64,
waker: Waker,
level: u8,
slot: u8,
prev: usize,
next: usize,
}
#[derive(Clone, Copy)]
struct Level {
occupied: u64,
heads: [usize; SLOTS],
}
pub(crate) struct Timers {
start: Instant,
elapsed: u64,
entries: Slab<Entry>,
levels: [Level; LEVELS],
seq: u64,
}
fn level_for(elapsed: u64, when: u64) -> usize {
let masked = ((elapsed ^ when) | SLOT_MASK).min(MAX_TICKS - 1);
let significant = 63 - masked.leading_zeros() as usize;
significant / 6
}
impl Timers {
pub(crate) fn new() -> Self {
Self::starting_at(Instant::now())
}
fn starting_at(start: Instant) -> Self {
Self {
start,
elapsed: 0,
entries: Slab::new(),
levels: [Level {
occupied: 0,
heads: [NIL; SLOTS],
}; LEVELS],
seq: 0,
}
}
fn tick_ceil(&self, t: Instant) -> u64 {
let nanos = t.saturating_duration_since(self.start).as_nanos();
nanos.div_ceil(NANOS_PER_TICK).min(u64::MAX as u128) as u64
}
fn tick_floor(&self, t: Instant) -> u64 {
let nanos = t.saturating_duration_since(self.start).as_nanos();
(nanos / NANOS_PER_TICK).min(u64::MAX as u128) as u64
}
fn insert(&mut self, deadline: Instant, waker: Waker) -> TimerKey {
let when = self
.tick_ceil(deadline)
.clamp(self.elapsed + 1, self.elapsed + MAX_TICKS - 1);
self.seq += 1;
let uid = self.seq;
let idx = self.entries.insert(Entry {
when,
uid,
waker,
level: 0,
slot: 0,
prev: NIL,
next: NIL,
});
self.link(idx);
(idx, uid)
}
fn update(&mut self, key: TimerKey, deadline: Instant, waker: &Waker) -> TimerKey {
match self.entries.get_mut(key.0) {
Some(entry) if entry.uid == key.1 => {
if !entry.waker.will_wake(waker) {
entry.waker = waker.clone();
}
key
}
_ => self.insert(deadline, waker.clone()),
}
}
fn remove(&mut self, key: TimerKey) -> Option<Waker> {
match self.entries.get(key.0) {
Some(entry) if entry.uid == key.1 => {}
_ => return None,
}
self.unlink(key.0);
self.entries.remove(key.0).map(|entry| entry.waker)
}
fn link(&mut self, idx: usize) {
let when = self.entries.get(idx).expect("timer entry").when;
let level = level_for(self.elapsed, when);
let slot = ((when >> (6 * level)) & SLOT_MASK) as usize;
let head = self.levels[level].heads[slot];
{
let entry = self.entries.get_mut(idx).expect("timer entry");
entry.level = level as u8;
entry.slot = slot as u8;
entry.prev = NIL;
entry.next = head;
}
if head != NIL {
self.entries.get_mut(head).expect("timer entry").prev = idx;
}
self.levels[level].heads[slot] = idx;
self.levels[level].occupied |= 1 << slot;
}
fn unlink(&mut self, idx: usize) {
let (level, slot, prev, next) = {
let entry = self.entries.get(idx).expect("timer entry");
(
entry.level as usize,
entry.slot as usize,
entry.prev,
entry.next,
)
};
if prev != NIL {
self.entries.get_mut(prev).expect("timer entry").next = next;
} else {
self.levels[level].heads[slot] = next;
if next == NIL {
self.levels[level].occupied &= !(1 << slot);
}
}
if next != NIL {
self.entries.get_mut(next).expect("timer entry").prev = prev;
}
}
fn next_expiration(&self) -> Option<(usize, usize, u64)> {
for level in 0..LEVELS {
let occupied = self.levels[level].occupied;
if occupied == 0 {
continue;
}
let slot_range = 1u64 << (6 * level);
let level_range = slot_range << 6;
let now_slot = ((self.elapsed >> (6 * level)) & SLOT_MASK) as u32;
let distance = occupied.rotate_right(now_slot).trailing_zeros();
let slot = ((now_slot + distance) & SLOT_MASK as u32) as usize;
let level_start = self.elapsed & !(level_range - 1);
let mut deadline = level_start + slot as u64 * slot_range;
if deadline <= self.elapsed {
deadline += level_range;
}
return Some((level, slot, deadline));
}
None
}
pub(crate) fn next_deadline(&self) -> Option<Instant> {
let (_, _, tick) = self.next_expiration()?;
Some(self.start + Duration::from_millis(tick))
}
pub(crate) fn expired(&mut self, now: Instant) -> Vec<Waker> {
let now_tick = self.tick_floor(now);
let mut fired = Vec::new();
while let Some((level, slot, deadline)) = self.next_expiration() {
if deadline > now_tick {
break;
}
let mut idx = std::mem::replace(&mut self.levels[level].heads[slot], NIL);
self.levels[level].occupied &= !(1 << slot);
self.elapsed = deadline;
while idx != NIL {
let (next, when) = {
let entry = self.entries.get(idx).expect("timer entry");
(entry.next, entry.when)
};
if when <= deadline {
fired.push(self.entries.remove(idx).expect("timer entry").waker);
} else {
self.link(idx);
}
idx = next;
}
}
if now_tick > self.elapsed {
self.elapsed = now_tick;
}
fired
}
pub(crate) fn clear(&mut self) -> Vec<Waker> {
for level in self.levels.iter_mut() {
*level = Level {
occupied: 0,
heads: [NIL; SLOTS],
};
}
self.entries
.take_all()
.into_iter()
.map(|entry| entry.waker)
.collect()
}
pub(crate) fn len(&self) -> usize {
self.entries.len()
}
}
#[must_use = "futures do nothing unless awaited"]
pub struct Sleep {
deadline: Instant,
key: Option<(TimerKey, usize)>,
}
impl Sleep {
pub fn deadline(&self) -> Instant {
self.deadline
}
pub fn reset(&mut self, deadline: Instant) {
self.unregister();
self.deadline = deadline;
}
fn unregister(&mut self) {
if let Some((key, uid)) = self.key.take() {
let waker = runtime::try_with_worker(|w| {
if w.shared.uid == uid {
w.timers
.try_borrow_mut()
.ok()
.and_then(|mut t| t.remove(key))
} else {
None
}
});
drop(waker);
}
}
}
impl Future for Sleep {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
if Instant::now() >= self.deadline {
self.unregister();
return Poll::Ready(());
}
let deadline = self.deadline;
let registered = self.key;
let key = runtime::with_worker(|w| {
let mut timers = w.timers.borrow_mut();
match registered {
Some((key, uid)) if uid == w.shared.uid => {
(timers.update(key, deadline, cx.waker()), uid)
}
_ => (timers.insert(deadline, cx.waker().clone()), w.shared.uid),
}
});
self.key = Some(key);
Poll::Pending
}
}
impl Drop for Sleep {
fn drop(&mut self) {
self.unregister();
}
}
impl fmt::Debug for Sleep {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Sleep")
.field("deadline", &self.deadline)
.finish()
}
}
pub fn sleep(duration: Duration) -> Sleep {
let now = Instant::now();
let deadline = now
.checked_add(duration)
.unwrap_or_else(|| now + Duration::from_secs(86400 * 365 * 30));
sleep_until(deadline)
}
pub fn sleep_until(deadline: Instant) -> Sleep {
Sleep {
deadline,
key: None,
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Elapsed;
impl fmt::Display for Elapsed {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("deadline has elapsed")
}
}
impl std::error::Error for Elapsed {}
impl From<Elapsed> for std::io::Error {
fn from(_: Elapsed) -> Self {
std::io::ErrorKind::TimedOut.into()
}
}
pub async fn timeout<F: Future>(duration: Duration, fut: F) -> Result<F::Output, Elapsed> {
let mut fut = pin!(fut);
let mut delay = pin!(sleep(duration));
poll_fn(|cx| {
if let Poll::Ready(value) = fut.as_mut().poll(cx) {
return Poll::Ready(Ok(value));
}
delay.as_mut().poll(cx).map(|()| Err(Elapsed))
})
.await
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::BTreeMap;
use std::sync::{Arc, Mutex};
use std::task::Wake;
struct Recorder(u64, Arc<Mutex<Vec<u64>>>);
impl Wake for Recorder {
fn wake(self: Arc<Self>) {
self.1.lock().unwrap().push(self.0);
}
}
struct Rng(u64);
impl Rng {
fn next(&mut self) -> u64 {
self.0 ^= self.0 << 13;
self.0 ^= self.0 >> 7;
self.0 ^= self.0 << 17;
self.0
}
fn below(&mut self, n: u64) -> u64 {
self.next() % n
}
}
fn run_model(seed: u64, steps: usize, max_delay_ms: u64, max_step_us: u64) {
let start = Instant::now();
let mut wheel = Timers::starting_at(start);
let mut rng = Rng(seed);
let fired = Arc::new(Mutex::new(Vec::new()));
let mut live: BTreeMap<u64, (u128, TimerKey)> = BTreeMap::new();
let mut now_ns: u128 = 0;
let mut next_id = 0u64;
for _ in 0..steps {
match rng.below(10) {
0..=4 => {
let delay_ns = rng.below(max_delay_ms * 1_000_000) as u128 + 1;
let deadline_ns = now_ns + delay_ns;
let waker = Waker::from(Arc::new(Recorder(next_id, fired.clone())));
let key = wheel.insert(start + Duration::from_nanos(deadline_ns as u64), waker);
live.insert(next_id, (deadline_ns, key));
next_id += 1;
}
5..=6 => {
if !live.is_empty() {
let nth = rng.below(live.len() as u64) as usize;
let id = *live.keys().nth(nth).unwrap();
let (_, key) = live.remove(&id).unwrap();
assert!(
wheel.remove(key).is_some(),
"live timer {id} missing from the wheel"
);
assert!(wheel.remove(key).is_none(), "timer {id} removed twice");
}
}
_ => {
now_ns += rng.below(max_step_us * 1_000) as u128 + 1;
let now = start + Duration::from_nanos(now_ns as u64);
for waker in wheel.expired(now) {
waker.wake();
}
let mut got = std::mem::take(&mut *fired.lock().unwrap());
got.sort_unstable();
let now_tick = now_ns / NANOS_PER_TICK;
let mut expected: Vec<u64> = live
.iter()
.filter(|(_, (deadline_ns, _))| {
deadline_ns.div_ceil(NANOS_PER_TICK) <= now_tick
})
.map(|(id, _)| *id)
.collect();
expected.sort_unstable();
assert_eq!(
got, expected,
"seed {seed}: wrong set of timers fired at {now_ns} ns"
);
for id in &got {
let (deadline_ns, key) = live.remove(id).unwrap();
assert!(deadline_ns <= now_ns, "timer {id} fired early");
assert!(
wheel.remove(key).is_none(),
"fired timer {id} still in the wheel"
);
}
}
}
assert_eq!(wheel.len(), live.len());
match (wheel.next_deadline(), live.values().map(|(d, _)| *d).min()) {
(None, None) => {}
(Some(wake), Some(earliest_ns)) => {
let wake_ns = wake.duration_since(start).as_nanos();
let earliest_tick_ns = earliest_ns.div_ceil(NANOS_PER_TICK) * NANOS_PER_TICK;
assert!(wake_ns <= earliest_tick_ns, "seed {seed}: would oversleep");
assert!(
wake_ns > (now_ns / NANOS_PER_TICK) * NANOS_PER_TICK,
"seed {seed}: wake-up in the past"
);
}
(wake, earliest) => {
panic!("seed {seed}: next_deadline {wake:?} vs model {earliest:?}")
}
}
}
let leftover = wheel.clear();
assert_eq!(leftover.len(), live.len());
assert_eq!(wheel.len(), 0);
assert!(wheel.next_deadline().is_none());
}
#[test]
fn wheel_matches_model_short_timers() {
for seed in 1..=40 {
run_model(seed, 4_000, 200, 3_000);
}
}
#[test]
fn wheel_matches_model_mixed_timers() {
for seed in 100..=130 {
run_model(seed, 4_000, 600_000, 5_000_000);
}
}
#[test]
fn wheel_matches_model_long_timers() {
for seed in 200..=215 {
run_model(seed, 3_000, 30 * 86_400_000, 7_200_000_000);
}
}
#[test]
fn far_future_timers_are_clamped_not_lost() {
let start = Instant::now();
let mut wheel = Timers::starting_at(start);
let fired = Arc::new(Mutex::new(Vec::new()));
let waker = Waker::from(Arc::new(Recorder(1, fired.clone())));
let key = wheel.insert(start + Duration::from_secs(1_000_000_000), waker);
assert_eq!(wheel.len(), 1);
assert!(wheel.next_deadline().is_some());
assert!(wheel.expired(start + Duration::from_secs(3600)).is_empty());
assert!(wheel.remove(key).is_some());
assert!(wheel.next_deadline().is_none());
}
}