subms-timer-wheel 0.9.1

submillisecond.com cookbook recipe - concurrency: subms-timer-wheel. Single-level hashed timer wheel with O(1) schedule and cancel.
Documentation
use super::*;

#[test]
fn scheduled_counter_increments_on_schedule() {
    let mut w: MeteredTimerWheel<u32> = MeteredTimerWheel::new(64);
    w.schedule(1, 1);
    w.schedule(2, 2);
    assert_eq!(w.metrics().scheduled, 2);
}

#[test]
fn num_slots_reflects_the_configured_wheel_size() {
    let w: MeteredTimerWheel<u32> = MeteredTimerWheel::new(256);
    assert_eq!(w.num_slots(), 256);
}

#[test]
fn fired_counter_matches_tick_results() {
    let mut w: MeteredTimerWheel<u32> = MeteredTimerWheel::new(64);
    w.schedule(1, 1);
    w.schedule(2, 2);
    w.schedule(2, 3);
    let fired_count: usize = (0..4).map(|_| w.tick().len()).sum();
    assert_eq!(fired_count as u64, w.metrics().fired);
    assert_eq!(w.metrics().fired, 3);
}

#[test]
fn cancelled_counter_only_increments_on_real_cancel() {
    let mut w: MeteredTimerWheel<u32> = MeteredTimerWheel::new(64);
    let id = w.schedule(5, 1);
    assert!(w.cancel(id));
    assert!(!w.cancel(9999));
    assert_eq!(w.metrics().cancelled, 1);
}

#[test]
fn ticks_counter_increments_per_tick() {
    let mut w: MeteredTimerWheel<u32> = MeteredTimerWheel::new(64);
    for _ in 0..7 {
        w.tick();
    }
    assert_eq!(w.metrics().ticks, 7);
}

#[test]
fn cascade_events_stays_zero_on_single_level_wheel() {
    let mut w: MeteredTimerWheel<u32> = MeteredTimerWheel::new(64);
    for d in 1..=20 {
        w.schedule(d, d as u32);
    }
    for _ in 0..30 {
        w.tick();
    }
    assert_eq!(w.metrics().cascade_events, 0);
}

#[test]
fn metrics_snapshot_independent_of_wheel_state() {
    let mut w: MeteredTimerWheel<u32> = MeteredTimerWheel::new(64);
    w.schedule(1, 1);
    let snap_a = w.metrics();
    w.schedule(2, 2);
    let snap_b = w.metrics();
    // The earlier snapshot is unchanged after a later schedule.
    assert_eq!(snap_a.scheduled, 1);
    assert_eq!(snap_b.scheduled, 2);
}

#[test]
fn schedule_fire_cancel_full_lifecycle_counters() {
    let mut w: MeteredTimerWheel<u32> = MeteredTimerWheel::new(64);
    let _a = w.schedule(2, 1);
    let b = w.schedule(2, 2);
    w.cancel(b);
    // tick #1: empty. tick #2: fires `1` (not `2` since cancelled).
    let fired_1 = w.tick();
    let fired_2 = w.tick();
    let total: Vec<u32> = fired_1.into_iter().chain(fired_2).collect();
    assert_eq!(total, vec![1]);
    let m = w.metrics();
    assert_eq!(m.scheduled, 2);
    assert_eq!(m.cancelled, 1);
    assert_eq!(m.fired, 1);
    assert_eq!(m.ticks, 2);
}

#[test]
fn reschedule_and_drain_have_their_own_counters() {
    let mut w: MeteredTimerWheel<u32> = MeteredTimerWheel::new(64);
    let a = w.schedule(2, 1);
    w.schedule(2, 2);
    assert!(w.reschedule(a, 6));
    assert!(!w.reschedule(999, 6), "unknown id does not count");
    assert_eq!(w.pending(), 2);

    let fired = w.advance(2);
    assert_eq!(fired, vec![2]);

    let drained = w.drain();
    assert_eq!(drained, vec![1]);

    let m = w.metrics();
    assert_eq!(m.scheduled, 2);
    assert_eq!(m.rescheduled, 1);
    assert_eq!(m.fired, 1);
    assert_eq!(m.drained, 1, "a drained timer is not a fired one");
    assert_eq!(m.ticks, 2);
}

#[test]
fn clear_counts_the_timers_it_dropped_and_try_schedule_meters() {
    let mut w: MeteredTimerWheel<u32> = MeteredTimerWheel::new(8);
    assert!(w.try_schedule(3, 1).is_ok());
    assert!(w.try_schedule(usize::MAX, 2).is_err());
    assert_eq!(
        w.metrics().scheduled,
        1,
        "a refused schedule is not metered"
    );
    assert_eq!(w.slot_len(3), 1);
    assert!(!w.is_empty());
    assert_eq!(w.max_delay(), 8 * i32::MAX as u64);
    w.clear();
    assert_eq!(w.metrics().drained, 1);
    assert!(w.is_empty());
}