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
+1
View File
@@ -153,6 +153,7 @@ pub fn spawn_under(supervisor: Pid, f: impl FnOnce() + Send + 'static) -> JoinHa
slot.outcome = None;
slot.waiters.clear();
slot.supervisor_channel = None;
slot.monitors.clear();
slot.pending_unpark = false;
slot.pending_io_result = None;
s.run_queue.push_back(pid);