fix: mild refactor and test coverage (#37)
Extract the timer wheel and watch registry from the worker into a reusable std-extension crate, generalize the worker around a RuntimeExtension factory, and decompose the monolithic runtime test file into focused suites.
- crates/std: extract TimerWheel (deterministic tick-counted one-shot/interval timers) and WatchRegistry (target-to-watcher death-notification index) out of src/worker.rs into reusable modules
- crates/std: add Ctx extension traits (CtxMonitoring, CtxNaming, CtxWatching, CtxTimers) and Runtime extension traits (RuntimeNaming, RuntimeWatching, RuntimeGroups) wiring monitor/name/watch/timer/group support
- src/worker.rs: replace the hard-coded timer/watch fields with a generic RuntimeExtension factory and add route_to_pool_or_remote for message routing (local pool, then cross-worker address map, then external inboxes)
- tests: split the 4622-line tests/runtime_api.rs into focused suites (actor_lifecycle, message_delivery, runtime_stress, std_extension) plus a shared tests/common/mod.rs harness, and drop watch_api.rs
- benches/fuzz: add runtime_benchmarks and adjust the runtime fuzz target
- tools/docs: add fn_complexity.py and loc_analysis.py analysis scripts and refresh the runtime and worker-thread docs
Signed-off-by: Zachery Aaron Shores-Chmielewski <zacheryasc@gmail.com>
2026-02-13 15:00:53 +00:00
|
|
|
// Shared types and helpers for runtime test files.
|
|
|
|
|
|
|
|
|
|
#![allow(dead_code, unused_imports)]
|
|
|
|
|
|
|
|
|
|
use std::sync::Arc;
|
2026-06-23 15:42:28 +00:00
|
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
fix: mild refactor and test coverage (#37)
Extract the timer wheel and watch registry from the worker into a reusable std-extension crate, generalize the worker around a RuntimeExtension factory, and decompose the monolithic runtime test file into focused suites.
- crates/std: extract TimerWheel (deterministic tick-counted one-shot/interval timers) and WatchRegistry (target-to-watcher death-notification index) out of src/worker.rs into reusable modules
- crates/std: add Ctx extension traits (CtxMonitoring, CtxNaming, CtxWatching, CtxTimers) and Runtime extension traits (RuntimeNaming, RuntimeWatching, RuntimeGroups) wiring monitor/name/watch/timer/group support
- src/worker.rs: replace the hard-coded timer/watch fields with a generic RuntimeExtension factory and add route_to_pool_or_remote for message routing (local pool, then cross-worker address map, then external inboxes)
- tests: split the 4622-line tests/runtime_api.rs into focused suites (actor_lifecycle, message_delivery, runtime_stress, std_extension) plus a shared tests/common/mod.rs harness, and drop watch_api.rs
- benches/fuzz: add runtime_benchmarks and adjust the runtime fuzz target
- tools/docs: add fn_complexity.py and loc_analysis.py analysis scripts and refresh the runtime and worker-thread docs
Signed-off-by: Zachery Aaron Shores-Chmielewski <zacheryasc@gmail.com>
2026-02-13 15:00:53 +00:00
|
|
|
|
|
|
|
|
pub use swactor::actor::{
|
2026-06-23 15:42:28 +00:00
|
|
|
ActorAddress, ActorExited, ActorInterface, CapabilitySet, Down, Environment,
|
|
|
|
|
EnvironmentBuilder, ExitReason, ExitValue, LogicalName, MonitorRef, ServiceBinding,
|
|
|
|
|
SpawnBuilder, SpawnTimestamp, StopReason,
|
fix: mild refactor and test coverage (#37)
Extract the timer wheel and watch registry from the worker into a reusable std-extension crate, generalize the worker around a RuntimeExtension factory, and decompose the monolithic runtime test file into focused suites.
- crates/std: extract TimerWheel (deterministic tick-counted one-shot/interval timers) and WatchRegistry (target-to-watcher death-notification index) out of src/worker.rs into reusable modules
- crates/std: add Ctx extension traits (CtxMonitoring, CtxNaming, CtxWatching, CtxTimers) and Runtime extension traits (RuntimeNaming, RuntimeWatching, RuntimeGroups) wiring monitor/name/watch/timer/group support
- src/worker.rs: replace the hard-coded timer/watch fields with a generic RuntimeExtension factory and add route_to_pool_or_remote for message routing (local pool, then cross-worker address map, then external inboxes)
- tests: split the 4622-line tests/runtime_api.rs into focused suites (actor_lifecycle, message_delivery, runtime_stress, std_extension) plus a shared tests/common/mod.rs harness, and drop watch_api.rs
- benches/fuzz: add runtime_benchmarks and adjust the runtime fuzz target
- tools/docs: add fn_complexity.py and loc_analysis.py analysis scripts and refresh the runtime and worker-thread docs
Signed-off-by: Zachery Aaron Shores-Chmielewski <zacheryasc@gmail.com>
2026-02-13 15:00:53 +00:00
|
|
|
};
|
2026-03-28 05:08:58 +00:00
|
|
|
pub use swactor::runtime::{Ctx, Inbox, Runtime, RuntimeConfig};
|
2026-02-24 09:12:28 +00:00
|
|
|
pub use swactor::std::{
|
2026-02-20 17:34:43 +00:00
|
|
|
ChildSpec, CtxCapabilities, CtxEnvironment, CtxGroups, CtxHandles, CtxLifecycle, CtxLineage,
|
|
|
|
|
CtxMonitoring, CtxNaming, CtxResources, CtxSelfStats, CtxSystem, CtxTimers, CtxWatching,
|
|
|
|
|
ResourceHandle, RestartPolicy, Router, RoutingStrategy, RuntimeGroups, RuntimeNaming,
|
|
|
|
|
RuntimeResources, RuntimeWatching, StdExtension, Supervisor, SupervisorStrategy,
|
fix: mild refactor and test coverage (#37)
Extract the timer wheel and watch registry from the worker into a reusable std-extension crate, generalize the worker around a RuntimeExtension factory, and decompose the monolithic runtime test file into focused suites.
- crates/std: extract TimerWheel (deterministic tick-counted one-shot/interval timers) and WatchRegistry (target-to-watcher death-notification index) out of src/worker.rs into reusable modules
- crates/std: add Ctx extension traits (CtxMonitoring, CtxNaming, CtxWatching, CtxTimers) and Runtime extension traits (RuntimeNaming, RuntimeWatching, RuntimeGroups) wiring monitor/name/watch/timer/group support
- src/worker.rs: replace the hard-coded timer/watch fields with a generic RuntimeExtension factory and add route_to_pool_or_remote for message routing (local pool, then cross-worker address map, then external inboxes)
- tests: split the 4622-line tests/runtime_api.rs into focused suites (actor_lifecycle, message_delivery, runtime_stress, std_extension) plus a shared tests/common/mod.rs harness, and drop watch_api.rs
- benches/fuzz: add runtime_benchmarks and adjust the runtime fuzz target
- tools/docs: add fn_complexity.py and loc_analysis.py analysis scripts and refresh the runtime and worker-thread docs
Signed-off-by: Zachery Aaron Shores-Chmielewski <zacheryasc@gmail.com>
2026-02-13 15:00:53 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ── Messages ────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct Ping {
|
|
|
|
|
pub reply_to: ActorAddress,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
|
|
|
pub struct Pong;
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct Increment {
|
|
|
|
|
pub reply_to: ActorAddress,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
|
|
|
pub struct Count(pub usize);
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct Forward {
|
|
|
|
|
pub value: usize,
|
|
|
|
|
pub reply_to: ActorAddress,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
|
|
|
pub struct Done(pub usize);
|
|
|
|
|
|
|
|
|
|
/// Ask an actor for its own address.
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct WhoAreYou {
|
|
|
|
|
pub reply_to: ActorAddress,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
|
|
|
pub struct MyAddr(pub ActorAddress);
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct PanicMsg;
|
|
|
|
|
|
|
|
|
|
/// Tells FanOutActor to distribute work.
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct FanOut {
|
|
|
|
|
pub count: usize,
|
|
|
|
|
pub reply_to: ActorAddress,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Message used in the chain test -- carries remaining hops and final reply address.
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct ChainMsg {
|
|
|
|
|
pub remaining: usize,
|
|
|
|
|
pub depth: usize,
|
|
|
|
|
pub reply_to: ActorAddress,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Actors ──────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/// Replies Pong to every Ping. Stateless.
|
|
|
|
|
pub struct PingPongActor;
|
|
|
|
|
|
|
|
|
|
impl ActorInterface for PingPongActor {
|
|
|
|
|
type Incoming = Ping;
|
|
|
|
|
type Response = Pong;
|
|
|
|
|
fn handle(&mut self, ctx: &Ctx, msg: Ping) {
|
|
|
|
|
let _ = ctx.send(msg.reply_to, Pong);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Counts Increment messages, replies Count(n) after each.
|
|
|
|
|
pub struct CounterActor {
|
|
|
|
|
pub count: usize,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ActorInterface for CounterActor {
|
|
|
|
|
type Incoming = Increment;
|
|
|
|
|
type Response = Count;
|
|
|
|
|
fn handle(&mut self, ctx: &Ctx, msg: Increment) {
|
|
|
|
|
self.count += 1;
|
|
|
|
|
let _ = ctx.send(msg.reply_to, Count(self.count));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Replies Done(value * 2).
|
|
|
|
|
pub struct DoubleActor;
|
|
|
|
|
|
|
|
|
|
impl ActorInterface for DoubleActor {
|
|
|
|
|
type Incoming = Forward;
|
|
|
|
|
type Response = Done;
|
|
|
|
|
fn handle(&mut self, ctx: &Ctx, msg: Forward) {
|
|
|
|
|
let _ = ctx.send(msg.reply_to, Done(msg.value * 2));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Spawns a DoubleActor child and forwards the work to it.
|
|
|
|
|
pub struct DelegatorActor;
|
|
|
|
|
|
|
|
|
|
impl ActorInterface for DelegatorActor {
|
|
|
|
|
type Incoming = Forward;
|
|
|
|
|
type Response = ();
|
|
|
|
|
fn handle(&mut self, ctx: &Ctx, msg: Forward) {
|
|
|
|
|
let child = ctx.spawn(DoubleActor).unwrap();
|
2026-06-23 15:42:28 +00:00
|
|
|
let _ = ctx.send(
|
|
|
|
|
child,
|
|
|
|
|
Forward {
|
|
|
|
|
value: msg.value,
|
|
|
|
|
reply_to: msg.reply_to,
|
|
|
|
|
},
|
|
|
|
|
);
|
fix: mild refactor and test coverage (#37)
Extract the timer wheel and watch registry from the worker into a reusable std-extension crate, generalize the worker around a RuntimeExtension factory, and decompose the monolithic runtime test file into focused suites.
- crates/std: extract TimerWheel (deterministic tick-counted one-shot/interval timers) and WatchRegistry (target-to-watcher death-notification index) out of src/worker.rs into reusable modules
- crates/std: add Ctx extension traits (CtxMonitoring, CtxNaming, CtxWatching, CtxTimers) and Runtime extension traits (RuntimeNaming, RuntimeWatching, RuntimeGroups) wiring monitor/name/watch/timer/group support
- src/worker.rs: replace the hard-coded timer/watch fields with a generic RuntimeExtension factory and add route_to_pool_or_remote for message routing (local pool, then cross-worker address map, then external inboxes)
- tests: split the 4622-line tests/runtime_api.rs into focused suites (actor_lifecycle, message_delivery, runtime_stress, std_extension) plus a shared tests/common/mod.rs harness, and drop watch_api.rs
- benches/fuzz: add runtime_benchmarks and adjust the runtime fuzz target
- tools/docs: add fn_complexity.py and loc_analysis.py analysis scripts and refresh the runtime and worker-thread docs
Signed-off-by: Zachery Aaron Shores-Chmielewski <zacheryasc@gmail.com>
2026-02-13 15:00:53 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Spawns a child chain: each level spawns the next until remaining == 0,
|
|
|
|
|
/// then the leaf replies Done(depth).
|
|
|
|
|
pub struct ChainActor;
|
|
|
|
|
|
|
|
|
|
impl ActorInterface for ChainActor {
|
|
|
|
|
type Incoming = ChainMsg;
|
|
|
|
|
type Response = Done;
|
|
|
|
|
fn handle(&mut self, ctx: &Ctx, msg: ChainMsg) {
|
|
|
|
|
if msg.remaining == 0 {
|
|
|
|
|
let _ = ctx.send(msg.reply_to, Done(msg.depth));
|
|
|
|
|
} else {
|
|
|
|
|
let child = ctx.spawn(ChainActor).unwrap();
|
|
|
|
|
let _ = ctx.send(
|
|
|
|
|
child,
|
|
|
|
|
ChainMsg {
|
|
|
|
|
remaining: msg.remaining - 1,
|
|
|
|
|
depth: msg.depth + 1,
|
|
|
|
|
reply_to: msg.reply_to,
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Spawns N DoubleActor children, sends Forward { value: i, reply_to } to each.
|
|
|
|
|
pub struct FanOutActor;
|
|
|
|
|
|
|
|
|
|
impl ActorInterface for FanOutActor {
|
|
|
|
|
type Incoming = FanOut;
|
|
|
|
|
type Response = ();
|
|
|
|
|
fn handle(&mut self, ctx: &Ctx, msg: FanOut) {
|
|
|
|
|
for i in 1..=msg.count {
|
|
|
|
|
let child = ctx.spawn(DoubleActor).unwrap();
|
2026-06-23 15:42:28 +00:00
|
|
|
let _ = ctx.send(
|
|
|
|
|
child,
|
|
|
|
|
Forward {
|
|
|
|
|
value: i,
|
|
|
|
|
reply_to: msg.reply_to,
|
|
|
|
|
},
|
|
|
|
|
);
|
fix: mild refactor and test coverage (#37)
Extract the timer wheel and watch registry from the worker into a reusable std-extension crate, generalize the worker around a RuntimeExtension factory, and decompose the monolithic runtime test file into focused suites.
- crates/std: extract TimerWheel (deterministic tick-counted one-shot/interval timers) and WatchRegistry (target-to-watcher death-notification index) out of src/worker.rs into reusable modules
- crates/std: add Ctx extension traits (CtxMonitoring, CtxNaming, CtxWatching, CtxTimers) and Runtime extension traits (RuntimeNaming, RuntimeWatching, RuntimeGroups) wiring monitor/name/watch/timer/group support
- src/worker.rs: replace the hard-coded timer/watch fields with a generic RuntimeExtension factory and add route_to_pool_or_remote for message routing (local pool, then cross-worker address map, then external inboxes)
- tests: split the 4622-line tests/runtime_api.rs into focused suites (actor_lifecycle, message_delivery, runtime_stress, std_extension) plus a shared tests/common/mod.rs harness, and drop watch_api.rs
- benches/fuzz: add runtime_benchmarks and adjust the runtime fuzz target
- tools/docs: add fn_complexity.py and loc_analysis.py analysis scripts and refresh the runtime and worker-thread docs
Signed-off-by: Zachery Aaron Shores-Chmielewski <zacheryasc@gmail.com>
2026-02-13 15:00:53 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Replies with its own address.
|
|
|
|
|
pub struct SelfAddrActor;
|
|
|
|
|
|
|
|
|
|
impl ActorInterface for SelfAddrActor {
|
|
|
|
|
type Incoming = WhoAreYou;
|
|
|
|
|
type Response = MyAddr;
|
|
|
|
|
fn handle(&mut self, ctx: &Ctx, msg: WhoAreYou) {
|
|
|
|
|
let _ = ctx.send(msg.reply_to, MyAddr(ctx.self_addr()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Panics on every message. Used to test panic isolation.
|
|
|
|
|
pub struct PanicActor;
|
|
|
|
|
|
|
|
|
|
impl ActorInterface for PanicActor {
|
|
|
|
|
type Incoming = PanicMsg;
|
|
|
|
|
type Response = ();
|
|
|
|
|
fn handle(&mut self, _ctx: &Ctx, _msg: PanicMsg) {
|
|
|
|
|
panic!("intentional test panic");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Increments a shared counter on each Ping. Used to observe processing from outside.
|
|
|
|
|
pub struct CountingPingActor {
|
|
|
|
|
pub counter: Arc<AtomicUsize>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ActorInterface for CountingPingActor {
|
|
|
|
|
type Incoming = Ping;
|
|
|
|
|
type Response = Pong;
|
|
|
|
|
fn handle(&mut self, ctx: &Ctx, msg: Ping) {
|
|
|
|
|
self.counter.fetch_add(1, Ordering::SeqCst);
|
|
|
|
|
let _ = ctx.send(msg.reply_to, Pong);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Null actor that accepts Ping but does nothing visible.
|
|
|
|
|
pub struct NullActor;
|
|
|
|
|
|
|
|
|
|
impl ActorInterface for NullActor {
|
|
|
|
|
type Incoming = Ping;
|
|
|
|
|
type Response = ();
|
|
|
|
|
fn handle(&mut self, _ctx: &Ctx, _msg: Ping) {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Actor that replies with its inbox address.
|
|
|
|
|
pub struct InboxReplyActor;
|
|
|
|
|
|
|
|
|
|
impl ActorInterface for InboxReplyActor {
|
|
|
|
|
type Incoming = Ping;
|
|
|
|
|
type Response = Pong;
|
|
|
|
|
fn handle(&mut self, ctx: &Ctx, msg: Ping) {
|
|
|
|
|
let _ = ctx.send(msg.reply_to, Pong);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/// Helper: construct a Runtime with StdExtension installed.
|
|
|
|
|
pub fn std_runtime(config: RuntimeConfig) -> Runtime {
|
|
|
|
|
Runtime::new(config).with_extension(Arc::new(StdExtension::new()))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Tick up to `max` times, returning as soon as `inbox` has a message.
|
|
|
|
|
pub fn tick_until_recv<M: swactor::actor::Message>(
|
|
|
|
|
rt: &Runtime,
|
|
|
|
|
inbox: &Inbox<M>,
|
|
|
|
|
max: usize,
|
|
|
|
|
) -> Option<M> {
|
|
|
|
|
for _ in 0..max {
|
|
|
|
|
rt.tick();
|
|
|
|
|
if let Some(msg) = inbox.try_recv() {
|
|
|
|
|
return Some(msg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Tick exactly `n` times (no inbox polling).
|
|
|
|
|
pub fn tick_n(rt: &Runtime, n: usize) {
|
|
|
|
|
for _ in 0..n {
|
|
|
|
|
rt.tick();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Tick `n` times, then drain all messages from the inbox.
|
|
|
|
|
pub fn tick_and_drain<M: swactor::actor::Message>(
|
|
|
|
|
rt: &Runtime,
|
|
|
|
|
inbox: &Inbox<M>,
|
|
|
|
|
ticks: usize,
|
|
|
|
|
) -> Vec<M> {
|
|
|
|
|
for _ in 0..ticks {
|
|
|
|
|
rt.tick();
|
|
|
|
|
}
|
|
|
|
|
std::iter::from_fn(|| inbox.try_recv()).collect()
|
|
|
|
|
}
|