//! Tests for explicit preemption via `smarm::check!()`. use smarm::{run, spawn}; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; #[test] fn check_yields_when_timeslice_expired() { // A single actor that drives the timeslice clock to zero manually, // then calls check!() and expects to yield. The scheduler has nothing // else to run, so it just re-queues us. To prove we actually yielded, // observe the run counter on the slot... we don't have one. So // instead: spawn a second actor that increments a counter and joins // it; verify both actors made progress in interleaved order under // forced timeslice expiry. let order: Arc>> = Arc::new(std::sync::Mutex::new(Vec::new())); let o1 = order.clone(); let o2 = order.clone(); run(move || { let a = spawn(move || { o1.lock().unwrap().push(b'A'); // Force the timeslice to be considered expired. smarm::preempt::expire_timeslice_for_test(); smarm::check!(); o1.lock().unwrap().push(b'a'); }); let b = spawn(move || { o2.lock().unwrap().push(b'B'); smarm::preempt::expire_timeslice_for_test(); smarm::check!(); o2.lock().unwrap().push(b'b'); }); a.join().unwrap(); b.join().unwrap(); }); // FIFO scheduling + forced preemption: A starts, expires, yields to B; // B starts, expires, yields to A; A finishes, B finishes. // Required: both uppercase letters appear before either lowercase. let v = order.lock().unwrap(); let pos_big_a = v.iter().position(|&c| c == b'A').unwrap(); let pos_big_b = v.iter().position(|&c| c == b'B').unwrap(); let pos_lit_a = v.iter().position(|&c| c == b'a').unwrap(); let pos_lit_b = v.iter().position(|&c| c == b'b').unwrap(); assert!(pos_big_a < pos_lit_a, "A's tail ran before B's head: {:?}", *v); assert!(pos_big_b < pos_lit_b, "B's tail ran before A's head: {:?}", *v); assert!(pos_big_a.max(pos_big_b) < pos_lit_a.min(pos_lit_b), "preemption didn't interleave: {:?}", *v); } #[test] fn check_is_a_noop_when_timeslice_not_expired() { // After a fresh resume, check!() should be cheap and not yield. Run // a single actor that calls check!() many times; it should complete // promptly. let count = Arc::new(AtomicU64::new(0)); let c = count.clone(); run(move || { for _ in 0..1_000 { smarm::check!(); c.fetch_add(1, Ordering::Relaxed); } }); assert_eq!(count.load(Ordering::Relaxed), 1_000); }