//! 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"); }); }