//! Process identifiers. //! //! Identity is `(index, generation)`: the index is a slot in the scheduler's //! actor table, the generation increments every time that slot is reused, so a //! stale id (right index, wrong generation) is a *detectable* error rather than //! a silent misdirection — the ABA problem solved without exhausting the id //! space. Those raw numbers live in [`RawPid`]. //! //! The public identity is the *typed* [`Pid`] (RFC 014): `RawPid` plus a //! phantom actor type, so a pid is simultaneously an identity and a direct, //! identity-bound address. `Pid` — the default — is the untyped pid //! used for identity-only plumbing and for actors with no single message type //! (raw `spawn`, gen_servers). Resolving a name yields the durable, re-resolving //! [`Name`] instead. use std::marker::PhantomData; /// The raw identity numbers, with no actor type. The key for everything that /// only cares about *which* actor: slab indexing, generation checks, and the /// heterogeneous monitor / link / pg tables (which hold actors of every type at /// once, so they cannot be parameterised by one). #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct RawPid { index: u32, generation: u32, } impl RawPid { #[inline] pub const fn new(index: u32, generation: u32) -> Self { Self { index, generation } } #[inline] pub const fn index(self) -> u32 { self.index } #[inline] pub const fn generation(self) -> u32 { self.generation } } impl std::fmt::Debug for RawPid { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Pid({}.{})", self.index, self.generation) } } impl std::fmt::Display for RawPid { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "<{}.{}>", self.index, self.generation) } } /// Phantom actor type for a pid that has no single message type: raw `spawn` /// actors, gen_servers (intrinsically multi-message, addressed via `GenServerRef`), /// and every identity-only context. Deliberately **not** [`Addressable`], so a /// typed `send` to a `Pid` does not compile; the runtime-checked /// `send_dyn` escape hatch (RFC 014 §4.6) is the sanctioned bare-pid path. pub enum Erased {} /// A process identifier parameterised by the actor's type `A` (default /// [`Erased`]). Wraps the raw `(index, generation)` plus a zero-sized phantom, /// so a `Pid` is both an identity and a direct, identity-bound address: when /// `A: Addressable`, a `send` delivers `A::Msg` to exactly the incarnation this /// pid names — no redirect (contrast the re-resolving [`Name`]). /// /// Equality, hashing, and formatting are the raw identity's; the phantom is /// `fn() -> A`, so `Pid` is unconditionally `Copy + Send + Sync` and borrows /// nothing from `A`. The trait impls are hand-written so no `A: Trait` bound /// leaks in from a `#[derive]`. pub struct Pid { raw: RawPid, _marker: PhantomData A>, } impl Pid { /// Build an untyped pid from raw numbers. The runtime mints identities /// here; typing happens at typed-actor boundaries via [`Pid::from_raw`]. #[inline] pub const fn new(index: u32, generation: u32) -> Self { Self { raw: RawPid::new(index, generation), _marker: PhantomData } } } impl Pid { /// Wrap a raw identity as a typed pid. Crate-internal: minting a typed pid /// from raw numbers asserts an actor's type *unchecked*, which is exactly /// what the typed API exists to avoid outside the runtime's own spawn / /// resolution paths. #[inline] pub(crate) const fn from_raw(raw: RawPid) -> Self { Self { raw, _marker: PhantomData } } /// The raw identity, dropping the actor type — the key for identity-only /// tables and internal plumbing. #[inline] pub const fn raw(self) -> RawPid { self.raw } /// Forget the actor type. #[inline] pub const fn erase(self) -> Pid { Pid::from_raw(self.raw) } /// Slot index in the actor table. #[inline] pub const fn index(self) -> u32 { self.raw.index() } /// Reuse generation of the slot (ABA guard). #[inline] pub const fn generation(self) -> u32 { self.raw.generation() } } /// Re-type an erased pid as `Pid` *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(pid: Pid) -> Pid { Pid::from_raw(pid.raw()) } impl Copy for Pid {} impl Clone for Pid { fn clone(&self) -> Self { *self } } impl PartialEq for Pid { fn eq(&self, other: &Self) -> bool { self.raw == other.raw } } impl Eq for Pid {} impl std::hash::Hash for Pid { fn hash(&self, state: &mut H) { self.raw.hash(state); } } impl std::fmt::Debug for Pid { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Debug::fmt(&self.raw, f) } } impl std::fmt::Display for Pid { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(&self.raw, f) } } /// An actor type with a single associated message type, so a [`Pid`] is a /// typed address. The raw channel layer has no such trait (actors are closures /// over channels) and `GenServer` is intrinsically multi-message (addressed via /// its own `GenServerRef`); this is the minimal hook that lets the single-message /// actors carry their message type in their pid. (RFC 014 §4.2.) pub trait Addressable: 'static { /// The message this actor receives. A `Pid` delivers `Self::Msg`. type Msg: Send + 'static; } /// A durable, re-resolving address: a static name plus a phantom message type /// `M` (RFC 014's `Name`). Declared as a constant and shared freely: /// /// ```ignore /// const COUNTER: Name = Name::new("counter"); /// ``` /// /// Unlike a [`Pid`], a `Name` is resolved through the registry on *every* send, /// so it always reaches whoever currently holds the name. pub struct Name { name: &'static str, _marker: PhantomData M>, } impl Name { /// Bind a static string as a typed name. `const`, so names live as /// associated constants at call sites. #[inline] pub const fn new(name: &'static str) -> Self { Self { name, _marker: PhantomData } } /// The underlying registry key. #[inline] pub const fn as_str(self) -> &'static str { self.name } } impl Copy for Name {} impl Clone for Name { fn clone(&self) -> Self { *self } } impl PartialEq for Name { fn eq(&self, other: &Self) -> bool { self.name == other.name } } impl Eq for Name {} impl std::hash::Hash for Name { fn hash(&self, state: &mut H) { self.name.hash(state); } } impl std::fmt::Debug for Name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Name<{}>({:?})", std::any::type_name::(), self.name) } } #[cfg(test)] mod typed_pid_tests { use super::*; // A stand-in actor type with one message type, exercising `Addressable`. struct Counter; struct CounterMsg; // used only as a phantom key; no variants needed impl Addressable for Counter { type Msg = CounterMsg; } fn msg_type_name() -> &'static str { std::any::type_name::() } #[test] fn typed_pid_is_a_copyable_identity() { let p = Pid::::from_raw(RawPid::new(3, 1)); let q = p; // Copy, not move assert_eq!(p.index(), 3); assert_eq!(p.generation(), 1); assert_eq!(p, q); // Same index, different generation = different incarnation. assert_ne!(p, Pid::::from_raw(RawPid::new(3, 2))); assert!(format!("{p:?}").starts_with("Pid(")); } #[test] fn erase_drops_the_type_but_keeps_identity() { let p = Pid::::from_raw(RawPid::new(7, 4)); assert_eq!(p.erase(), Pid::new(7, 4)); // Pid assert_eq!(p.raw(), RawPid::new(7, 4)); } #[test] fn name_is_a_copyable_string_token() { const COUNTER: Name = Name::new("counter"); let n = COUNTER; // Copy assert_eq!(n.as_str(), "counter"); assert_eq!(n, COUNTER); assert_ne!(n, Name::::new("other")); assert!(format!("{n:?}").contains("\"counter\"")); } #[test] fn addressable_exposes_the_message_type() { assert!(msg_type_name::().ends_with("CounterMsg")); } // Identity tokens must be usable across threads. #[test] fn tokens_are_send_sync_without_key_bounds() { fn assert_send_sync() {} assert_send_sync::>(); assert_send_sync::>(); assert_send_sync::>(); } }