diff --git a/examples/ctx_switch_probe.rs b/examples/ctx_switch_probe.rs new file mode 100644 index 0000000..4633693 --- /dev/null +++ b/examples/ctx_switch_probe.rs @@ -0,0 +1,47 @@ +//! A deliberately small, deterministic smarm program for inspecting the +//! naked-asm context switch under a debugger (llmdbg). +//! +//! Design goals that make it debugger-friendly: +//! - Exactly one scheduler thread (`Config::exact(1)`) so the whole run is +//! single-OS-threaded — llmdbg is single-threaded-debuggee only today. +//! - A known, small number of voluntary yields, so the number of +//! `switch_to_actor` / `switch_to_scheduler` transitions is predictable. +//! - Frame pointers preserved (see the [profile] note in how we build it), +//! so llmdbg's `finish()` and frame-pointer assumptions hold. +//! - No I/O, no timers, no channels — just the scheduler and the context +//! switch, to keep the instruction stream centered on the asm we care +//! about. +//! +//! Two actors each yield `N_YIELDS` times, printing a counter. Run normally +//! it just prints an interleaving. Run under llmdbg with breakpoints on +//! `smarm::context::switch_to_actor_asm` and +//! `smarm::context::switch_to_scheduler` to watch the stack pointer hand off +//! between the scheduler stack and each actor's mmap'd stack. + +use smarm::{init, Config}; + +const N_YIELDS: usize = 3; + +fn main() { + // Single scheduler thread: one OS thread, cooperative only. + let rt = init(Config::exact(1)); + rt.run(|| { + let a = smarm::spawn(|| { + for i in 0..N_YIELDS { + println!("actor A tick {i}"); + smarm::yield_now(); + } + println!("actor A done"); + }); + let b = smarm::spawn(|| { + for i in 0..N_YIELDS { + println!("actor B tick {i}"); + smarm::yield_now(); + } + println!("actor B done"); + }); + a.join().expect("A joined"); + b.join().expect("B joined"); + println!("root done"); + }); +}