feat(monitor): unidirectional one-shot process monitors

`monitor(pid) -> Receiver<Down>` lets any actor watch any other and
receive a single Down{pid, reason} when it terminates. Generalizes the
existing single supervisor_channel (which is just a hard-wired monitor
the parent installs at spawn).

- src/monitor.rs: Down / DownReason{Exit,Panic,NoProc} + monitor().
  Payload-free: a panic payload has one owner and goes to the joiner via
  JoinError, so monitors learn only that it panicked. Monitoring an
  already-gone pid yields NoProc immediately.
- runtime: Slot grows `monitors: Vec<Sender<Down>>`; finalize_actor
  drains and notifies them outside the shared lock (send can unpark a
  receiver, which re-takes the non-reentrant mutex). Cleared in
  reclaim_slot and on slot reuse at spawn.
- Registration and finalize both serialize on the shared mutex, so a
  target alive at registration always delivers a real Down (no race
  between liveness check and registration).

tests/monitor.rs: Exit, Panic, NoProc-on-dead-target, and fan-out to
multiple monitors. Full suite green.
This commit is contained in:
smarm-dev
2026-06-07 21:51:56 +00:00
parent 14dc7a79cf
commit 461de2c451
5 changed files with 209 additions and 4 deletions
+2
View File
@@ -22,6 +22,7 @@ pub mod supervisor;
pub mod timer;
pub mod io;
pub mod mutex;
pub mod monitor;
pub mod runtime;
pub mod trace;
@@ -37,6 +38,7 @@ static ALLOCATOR: preempt::PreemptingAllocator = preempt::PreemptingAllocator;
// ---------------------------------------------------------------------------
pub use channel::{channel, Receiver, RecvError, Sender};
pub use monitor::{monitor, Down, DownReason};
pub use mutex::{LockTimeout, Mutex, MutexGuard};
pub use pid::Pid;
pub use runtime::{init, Config, Runtime};