Phase 4 — Process management
Phase 4 is the ergonomic layer that makes a process system livable: stable names, delayed messages, and a clean way to stop everything at once.
Why this phase
Phases 1–3 delivered a real actor model — processes spawn, message each other, crash, and recover. But raw pids are ephemeral. Pass one around long enough and it may point to a dead process. A real system needs stable names you can look up by string, timers that fire without dedicating a process, and a shutdown path that doesn't leave lingering tasks.
These aren't glamorous features. They're the difference between "this works in a demo" and "this is something you can build on." Phase 4 closes that gap.
What shipped
- Named registry — a sharded
DashMap(name → pid) making registration and lookup concurrent and lock-free in the common case:register(name, pid),whereis(name),unregister(name), andsend_named(name, msg)— resolve and deliver in one step. Names are reaped automatically when the owning process exits; a dead name never resolves to a stale pid. - Timers —
send_after(pid, delay, msg) -> TimerRef— delivers a message after a delay, on Tokio's hierarchical timer wheel. Thousands of pending timers cost almost nothing.TimerRef::cancel()stops a not-yet-fired timer. - Graceful
shutdown() -> usize— kills every live process and returns the count. The node stops cleanly.
Design highlights
- Registry sharding matches the process table. Naming never becomes a global lock — the same concurrency model as Phase 1's
DashMapprocess table, applied to names. - Timers ride Tokio's wheel, not a task-per-timer. Pending timers are nearly free, so
send_afterscales with process count — not with "how many timers are live." - Self-cleaning names. Deregistration is part of the same Drop path that reaps the table entry from Phase 1. There is no stale-name window to manage by hand, and no
unregister_on_exitcall to remember. send_namedis atomic. Resolve + deliver happen in a single registry operation. No TOCTOU between finding the pid and sending to it.
What this unlocks
With the registry, processes publish stable service names and clients look them up — no pid threading through the whole application. send_named("logger", msg) works whether the logger was spawned 100ms ago or 10 minutes ago.
Timers enable timeout patterns, periodic heartbeats, and deadline-based cancellation without spinning up a dedicated timer process. send_after + recv_match is the building block for receive_timeout — Erlang's receive … after N → … — which arrives in the Wasm ABI in Phase 7.
Try it
cargo test -p rusm-otp registry # register / whereis / auto-reap on process exit
cargo test -p rusm-otp timer # send_after fires; cancel stops itStatus
Phase complete. Registry is concurrent, lock-free, self-cleaning. Timers use zero per-timer overhead. No new dashboard scenario; this phase rounds out the single-node API surface.
Next: Phase 5 — connectivity: TCP listen/connect, one process per connection, connection-storm goes live.