pub struct Mailbox { /* private fields */ }Expand description
A flow’s inbox, plus (when the owning flow is blocked on
Receive / ReceiveMatch / Ask with nothing to read) the parked flow itself.
§Why the flow lives inside its own mailbox while waiting
Internally, a flow is reachable through its super::process::FlowId, which
resolves (via [super::directory::Directory]) to this Mailbox. Bytecode
does not address by Pid anymore (FlowCap): Send / Ask resolve a
Cap to a FlowId first, then look up here. Host crate::Runtime::send
still uses FlowId directly (trusted).
Storing the blocked Box<Flow> directly in MailboxInner::parked, behind
the same mutex that guards the message queue, turns “deliver a message and
wake the receiver if it was waiting” into a single critical section — which
is what actually prevents the classic lost-wakeup race:
racing without a shared lock:
receiver: queue.pop() -> None
sender: queue.push(msg); wake(receiver) // receiver isn't parked yet!
receiver: park() // ...and now sleeps forever
with both steps under one mutex (what this type does):
receiver: lock; queue.pop() -> None; store self in `parked`; unlock
sender: lock; parked.take() -> Some(receiver); unlock; wake(receiver)Because “check the queue” and “become parked” happen atomically with
respect to “push and check for a parked receiver”, there is no window
where a message can be pushed without either landing in the queue for a
later Receive or immediately waking an already-parked one.
§Selective wait (ReceiveMatch / Ask)
When parked with a non-[WaitFilter::Any] filter, only a hop that
satisfies the filter wakes the flow. Other hops are appended to the
queue and the waiter stays parked (FIFO skip, never drop).
Implementations§
Source§impl Mailbox
impl Mailbox
pub fn new() -> Self
Sourcepub fn push(&self, value: Value) -> Result<Delivery, RuntimeError>
pub fn push(&self, value: Value) -> Result<Delivery, RuntimeError>
Push value. If a flow is currently parked on this mailbox
and the hop satisfies its wait filter, it is atomically removed and
returned via Delivery::Handoff. Otherwise the hop is queued
(and a selective waiter stays parked).
Mutex poison → RuntimeError (fail-closed; do not continue on
inconsistent shared state).
Sourcepub fn try_pop(&self) -> Result<Option<Value>, RuntimeError>
pub fn try_pop(&self) -> Result<Option<Value>, RuntimeError>
Non-blocking pop of the front hop (classic Receive).
Sourcepub fn try_pop_match(&self, tag: u16) -> Result<Option<Value>, RuntimeError>
pub fn try_pop_match(&self, tag: u16) -> Result<Option<Value>, RuntimeError>
Non-blocking selective pop by application tag (ReceiveMatch).
Sourcepub fn park(
&self,
flow: Box<Flow>,
) -> Result<Result<(), Box<Flow>>, RuntimeError>
pub fn park( &self, flow: Box<Flow>, ) -> Result<Result<(), Box<Flow>>, RuntimeError>
Atomically re-check the queue and, if still empty, store flow
as parked (classic Receive — any hop wakes).
Outer Result is infrastructure (mutex poison). Inner Result is
the lost-wakeup race: Err(flow) means a message arrived between
the worker’s try_pop and this call — resume immediately with the
stashed pending message rather than parking forever.
Sourcepub fn park_match(
&self,
flow: Box<Flow>,
tag: u16,
) -> Result<Result<(), Box<Flow>>, RuntimeError>
pub fn park_match( &self, flow: Box<Flow>, tag: u16, ) -> Result<Result<(), Box<Flow>>, RuntimeError>
Like park, but only a hop with Message.tag == tag
ends the wait. Non-matching hops already in the queue are left
untouched (FIFO skip).
Sourcepub fn take_parked(&self) -> Result<Option<Box<Flow>>, RuntimeError>
pub fn take_parked(&self) -> Result<Option<Box<Flow>>, RuntimeError>
Attempt to take a timed-out parked flow back out, used by the
timer wheel when a ReceiveTimeout deadline fires. Returns None
if the flow was already woken by a Send in the meantime.