mailbox: typed-path producers, by-name gen_servers, root-exit teardown (RFC 014)
Implements the new addressing surface the examples in examples/ specify: - spawn_addr::<A>(FnOnce(Receiver<A::Msg>)) -> Pid<A>: the typed-path producer. Makes the inbox, spawns the body with its receiver, and publishes the sender from the PARENT side (registry::install_for) before returning the pid, so an immediate send_to always resolves (no race on the body installing itself). Detached, like ServerBuilder::start. - lookup_as / pick_as / members_as: re-type an erased pid as Pid<A> over the heterogeneous registry/pg stores, sharing one helper (pid::assert_type). Unchecked but sound: routing is by message TypeId, so a wrong A degrades to SendError::NoChannel on the next send, never a misdelivery. - dispatch::<A>(group, msg) -> Result<Pid<A>, SendError<A::Msg>>: pick_as + send_to, with the message handed back on failure. New SendError::NoMember variant for the empty/all-dead group case. - gen_server naming: ServerName<G> backed internally by the registry's existing typed-channel store keyed by TypeId::of::<Envelope<G>>() — Envelope stays private, no separate directory. Type-state NamedServerBuilder<G> keeps the current infallible ServerBuilder::start() untouched; its start() is fallible (parent-side register, NameTaken). Free call/cast/whereis_server resolve per use. ServerRef::shutdown (+ free shutdown) is the sys-style synchronous stop. - root-exit teardown: the run's initial actor is recorded as root; when it finalizes it flags root_exited, and the scheduler's idle verdict then stops the parked-forever remainder (a one-shot RootDrain sweep). Deferred to the idle point on purpose: the run queue drains first, so actors with queued work finish rather than unwinding on the stop. This lets a named-server daemon (or any pinned actor) wind the run down instead of hanging on live_actors > 0. request_stop is refactored to a request_stop_inner core so the sweep can drive it from inside the runtime without re-borrowing the thread-local. Lib + tests + examples build warning-free; full suite green.
This commit is contained in:
@@ -97,7 +97,8 @@
|
||||
//! discipline as `demonitor`).
|
||||
|
||||
use crate::monitor::{demonitor, monitor, Monitor};
|
||||
use crate::pid::Pid;
|
||||
use crate::pid::{assert_type, Addressable, Pid};
|
||||
use crate::registry::{send_to, SendError};
|
||||
use crate::scheduler::with_runtime;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -408,6 +409,43 @@ pub fn pick(group: &str) -> Option<Pid> {
|
||||
picked
|
||||
}
|
||||
|
||||
/// Typed `pick`: one live member of `group` as a [`Pid<A>`] (RFC 014 §4.4).
|
||||
/// For a homogeneous pool every member is an `A`, so the picked member comes
|
||||
/// back typed and dispatch is an ordinary compile-checked [`send_to`] rather
|
||||
/// than the [`send_dyn`](crate::send_dyn) escape hatch. Re-types via the
|
||||
/// unchecked [`assert_type`] primitive — a wrong `A` degrades to
|
||||
/// [`SendError::NoChannel`] on the next send, never a misdelivery.
|
||||
///
|
||||
/// Panics if called outside `Runtime::run()`.
|
||||
pub fn pick_as<A: Addressable>(group: &str) -> Option<Pid<A>> {
|
||||
pick(group).map(assert_type::<A>)
|
||||
}
|
||||
|
||||
/// Typed `members`: every live member of `group` as a [`Pid<A>`], same
|
||||
/// unchecked re-type as [`pick_as`]. Fan-out stays compile-checked end to end.
|
||||
///
|
||||
/// Panics if called outside `Runtime::run()`.
|
||||
pub fn members_as<A: Addressable>(group: &str) -> Vec<Pid<A>> {
|
||||
members(group).into_iter().map(assert_type::<A>).collect()
|
||||
}
|
||||
|
||||
/// Pick a live member of `group` and send it `msg` in one step, returning the
|
||||
/// member it reached on success. The pick-a-live-member-and-send combinator
|
||||
/// over [`pick_as`] + [`send_to`].
|
||||
///
|
||||
/// Errors hand `msg` back undelivered: [`SendError::NoMember`] if the pool is
|
||||
/// empty (or all-dead), otherwise whatever the underlying [`send_to`] returns
|
||||
/// (e.g. the picked member died in the window between pick and send →
|
||||
/// [`SendError::Dead`]).
|
||||
///
|
||||
/// Panics if called outside `Runtime::run()`.
|
||||
pub fn dispatch<A: Addressable>(group: &str, msg: A::Msg) -> Result<Pid<A>, SendError<A::Msg>> {
|
||||
match pick_as::<A>(group) {
|
||||
Some(pid) => send_to::<A>(pid, msg).map(|()| pid),
|
||||
None => Err(SendError::NoMember(msg)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user