Adds a Strategy enum (OneForOne default, OneForAll, RestForOne) selected via OneForOne::strategy(). The triggering child's Restart policy still decides whether anything restarts; the strategy decides which siblings are cycled with it: - OneForAll : all live siblings - RestForOne: siblings started after the failed child Group restarts stop the affected survivors cooperatively (request_stop) in reverse start order, await each one's termination signal on the existing funnel (no new channel, no select), then restart the whole set in start order. Signals that arrive for a child we aren't currently stopping are stashed and replayed by the main loop. A Stopped signal now counts as abnormal for the restart decision. On giving up (intensity cap) or any exit with survivors, an ordered shutdown stops the remaining children in reverse start order instead of leaking them; on the normal all-settled exit it's a no-op. The struct keeps the OneForOne name for compatibility (existing tests unchanged); rename is a later refactor.
smarm
SMARM — Smarm, Marks Actor Runtime Machinery. A proof-of-concept green-thread actor runtime for Rust.
Implements the core ideas in Achitecture.md: green-thread actors on a
shared heap, scheduled cooperatively, communicating only by Send messages.
Erlang's isolation model without Erlang's copying GC, Rust's zero-copy
ownership transfers without async's function colouring.
The scheduler is multi-threaded — one OS thread per available CPU, all drawing
from a shared run queue. The single-threaded run() entry point is kept as a
convenience wrapper around runtime::init(Config::exact(1)).run(f).
What's here
| Module | What it does |
|---|---|
stack |
mmap'd growable stack with guard page; SIGSEGV on overflow |
context |
#[naked] x86-64 context-switch shims, callee-saved regs only |
preempt |
Allocator-driven preemption; check!() macro for no-alloc loops |
pid |
(index, generation) PIDs; stale handles are detectable, not silent |
actor |
Trampoline + catch_unwind boundary at the actor entry point |
scheduler |
Run queue, slot table, spawn/join, parking, idle path |
channel |
Unbounded MPSC channel; recv parks the actor |
mutex |
Mutex<T> with mandatory timeout; FIFO waiters; parks the green thread |
timer |
Min-heap of (deadline, reason); Sleep and WaitTimeout reasons |
io |
block_on_io for blocking work; wait_readable/wait_writable + read/write via epoll |
supervisor |
Signal::Exit / Signal::Panic delivered to a parent actor's mailbox |
Quick taste
use smarm::{run, spawn, channel};
run(|| {
let (tx, rx) = channel::<i64>();
let h = spawn(move || {
for _ in 0..3 {
let v = rx.recv().unwrap();
println!("got {v}");
}
});
for v in 1..=3i64 {
tx.send(v).unwrap();
}
h.join().unwrap();
});
Layout
src/
stack.rs context.rs preempt.rs pid.rs actor.rs
scheduler.rs channel.rs mutex.rs timer.rs io.rs supervisor.rs
lib.rs
tests/
per-module integration tests
benches/
primes.rs fan-out/fan-in compute, vs tokio current_thread
Building and running
Standard Cargo. Requires Rust 1.95 or newer (the #[naked] attribute went stable
in 1.88; we use a few unrelated post-1.88 features). x86-64 Linux only —
ARM64 and macOS are on the deferred list because of the assembly shim and the
epoll dependency.
cargo test # all tests
cargo test --test mutex # one module
cargo bench # primes benchmark vs tokio
What's not here
See the Defer section of Architecture.md.
restart-intensity caps, join! for handle groups, stack growth via remap,
hierarchical timer wheel, fd-wait timeouts, Signal::Timeout. Each is
mechanism we know how to add; none belongs in this iteration.
Docs
| Document | What it covers |
|---|---|
Architecture.md |
Design intent, runtime model, and deferred work |
smarm - Deep Dive.html |
Generated walkthrough of the system; good starting point |
BENCHMARKS_AND_TUNING.md |
Where smarm wins and loses vs tokio, preemption knob recommendations |
benchmarks.md |
Raw benchmark results, methodology, and tuning experiment log |
Contributing
This is a personal proof-of-concept. There's no PR workflow. If you fork it and do something interesting, just send me an email. If it's nice, I'll upstream the changes.