refactor: centralize runtime logic (v0.4)

Extract scheduler responsibilities into a dedicated Runtime component:
- src/runtime.rs: New centralized control flow (669 lines)
- src/scheduler.rs: Simplified to task queue & preemption management
- tests/runtime.rs: Comprehensive runtime test suite
- benches/multi_scheduler.rs: Multi-runtime scheduling benchmarks
- Improves modularity and enables per-runtime configuration
This commit is contained in:
Claude
2026-05-23 16:09:32 +00:00
parent 8cbef1dfc1
commit e9fdbb1160
10 changed files with 1694 additions and 919 deletions
+10 -27
View File
@@ -2,14 +2,12 @@
//!
//! Erlang-style green-thread actor concurrency for Rust.
//!
//! Single-threaded for now: one scheduler, one OS thread. The scheduler
//! cooperatively interleaves green-thread actors with hand-rolled context
//! switches. Actors communicate by sending `Send` messages over channels;
//! every actor has a supervisor, which is itself just an actor with a
//! `Receiver<Signal>`. Synchronisation primitives — `Mutex<T>` with
//! mandatory lock timeouts, channel `recv`, `sleep`, and epoll-backed
//! `wait_readable`/`wait_writable` — all park the green thread, never
//! the OS thread.
//! Multi-threaded: N scheduler OS threads (default: one per CPU) share a
//! single global run queue behind a `Mutex`. Actors communicate by sending
//! `Send` messages over channels; every actor has a supervisor. Synchronisation
//! primitives — `Mutex<T>` with mandatory lock timeouts, channel `recv`,
//! `sleep`, and epoll-backed `wait_readable`/`wait_writable` — all park the
//! green thread, never the OS thread.
//!
//! See `LOOM.md` for the design intent and the deferred-for-later list.
@@ -24,13 +22,10 @@ pub mod supervisor;
pub mod timer;
pub mod io;
pub mod mutex;
pub mod runtime;
// ---------------------------------------------------------------------------
// Global allocator
//
// The preempting allocator wraps `System`. While `PREEMPTION_ENABLED` is
// false (the default outside an actor) it adds one branch per allocation
// and no syscalls. The scheduler flips it on per-resume.
// ---------------------------------------------------------------------------
#[global_allocator]
@@ -43,31 +38,19 @@ static ALLOCATOR: preempt::PreemptingAllocator = preempt::PreemptingAllocator;
pub use channel::{channel, Receiver, RecvError, Sender};
pub use mutex::{LockTimeout, Mutex, MutexGuard};
pub use pid::Pid;
pub use runtime::{init, Config, Runtime};
pub use scheduler::{
block_on_io, run, self_pid, sleep, spawn, spawn_under, wait_readable, wait_writable,
yield_now, JoinError, JoinHandle,
};
// `read` and `write` would shadow heavily-used names if re-exported at the
// crate root; users reach for them as `smarm::scheduler::read` /
// `smarm::scheduler::write` instead. May reshuffle into a `smarm::io`
// surface in a future pass.
pub use supervisor::Signal;
// ---------------------------------------------------------------------------
// check!() — explicit preemption point for tight no-alloc loops.
// check!()
// ---------------------------------------------------------------------------
/// Voluntarily check whether this actor's timeslice has expired, yielding
/// if so. Drop this into hot compute loops that don't allocate (heap or
/// large stack frames) — without it, such loops monopolise the scheduler
/// until they return.
///
/// Decrements the same per-actor event counter as the heap allocator's
/// preemption hook, so the check rate is identical regardless of whether
/// the actor is alloc-heavy, check-heavy, or mixed.
///
/// No-op outside an actor (the runtime's `PREEMPTION_ENABLED` flag is
/// false there).
/// if so.
#[macro_export]
macro_rules! check {
() => {