//! RFC 018 scheduler park/wake — observable-behavior guards. //! //! These pin the two timer-latency properties the park/wake swap must //! preserve or introduce: //! //! - `sleep_fires_under_saturation`: due timers fire even when every //! scheduler is busy (nobody parked ⇒ no timekeeper) — the busy-path //! due-check, ratified design point (a). The old drain phase gave this //! for free (timers drained every loop iteration); the new design must //! not lose it. //! - `submillisecond_sleep_is_prompt`: a sub-ms sleep completes promptly. //! Under the old wake pipe, `poll_wake`'s `as_millis` truncation turned //! sub-ms deadlines into 0ms busy-polls (correct wall time, pathological //! CPU); under park/wake the futex timespec carries full nanosecond //! precision. use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::time::{Duration, Instant}; #[test] fn sleep_fires_under_saturation() { let rt = smarm::runtime::init(smarm::runtime::Config::exact(4)); rt.run(|| { let stop = Arc::new(AtomicBool::new(false)); let mut spinners = Vec::new(); // 8 spinners over 4 schedulers: the run queue never empties, so no // scheduler ever parks and no timekeeper exists. Only the busy-path // due-check can fire the sleeper's timer before the spinners quit. for _ in 0..8 { let stop = stop.clone(); spinners.push(smarm::spawn(move || { let t0 = Instant::now(); while !stop.load(Ordering::Relaxed) && t0.elapsed() < Duration::from_secs(5) { smarm::yield_now(); } })); } let t0 = Instant::now(); smarm::sleep(Duration::from_millis(10)); let dt = t0.elapsed(); stop.store(true, Ordering::Relaxed); for s in spinners { let _ = s.join(); } assert!( dt < Duration::from_millis(500), "10ms sleep took {dt:?} under scheduler saturation — busy-path \ timer firing is broken (timekeeper-only firing stalls under load)" ); }); } #[test] fn submillisecond_sleep_is_prompt() { let rt = smarm::runtime::init(smarm::runtime::Config::exact(2)); rt.run(|| { // Warm one iteration, then measure. smarm::sleep(Duration::from_micros(500)); let t0 = Instant::now(); smarm::sleep(Duration::from_micros(500)); let dt = t0.elapsed(); assert!(dt >= Duration::from_micros(400), "woke early: {dt:?}"); assert!( dt < Duration::from_millis(100), "500µs sleep took {dt:?} — sub-ms deadline handling is broken" ); }); }