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:
+16
@@ -119,6 +119,22 @@ impl<A> Pid<A> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Re-type an erased pid as `Pid<A>` *unchecked* — the one shared primitive
|
||||
/// behind `lookup_as` / `pick_as` / `members_as` (RFC 014 §4.4). The registry
|
||||
/// and pg stores are heterogeneous in `A` (they hold actors of every type at
|
||||
/// once), so resolving them yields a bare [`Pid`]; recovering the typed address
|
||||
/// is necessarily an assertion the store cannot make for us.
|
||||
///
|
||||
/// **Not unsound.** Delivery routes on the message's [`TypeId`](std::any::TypeId)
|
||||
/// (every send path keys the channel store by it), so a wrong `A` here does not
|
||||
/// mis-deliver: the next [`send_to`](crate::send_to) finds no channel for
|
||||
/// `A::Msg` on that actor and returns [`SendError::NoChannel`](crate::SendError::NoChannel).
|
||||
/// A mistyped pid degrades to a clean send error, never a silent misroute.
|
||||
#[inline]
|
||||
pub(crate) fn assert_type<A>(pid: Pid) -> Pid<A> {
|
||||
Pid::from_raw(pid.raw())
|
||||
}
|
||||
|
||||
impl<A> Copy for Pid<A> {}
|
||||
impl<A> Clone for Pid<A> {
|
||||
fn clone(&self) -> Self {
|
||||
|
||||
Reference in New Issue
Block a user