Expand description
CoreMailbox — the Send + Sync bridge between autonomous async
producers (timer tasks) and an owned, relocatable crate::node::Core.
§Why this exists (D223 / D225 / D227 / D230)
Under the actor / work-stealing model (D221) a Core owns its state
cell by value and moves between workers — so a long-lived async
task (e.g. a tokio::spawn-ed timer loop) can no longer hold
&Core / Arc<C> / Weak<C> to call Core::emit directly (that
was the deleted WeakCore path; D223 forbids Weak<C> back-refs).
Instead the task holds an Arc<CoreMailbox> (Send + Sync) and
posts a (NodeId, HandleId) emit request. The mailbox is drained
owner-side by the synchronous crate::node::Core::drain_mailbox
(applied via the existing sync Core::emit — no async in Core,
honoring the locked “Core never async” invariant). The drain point is
the embedder’s existing advance/pump site (test harness TestRuntime
advance helper, napi pump): timer tasks already require the host
runtime to be advanced before they fire, so draining there is
behaviour-identical to the old autonomous Weak::upgrade → core.emit (D230).
§Shape (D227 full)
- Op queue — FIFO of
MailboxOps, applied owner-side. closed— set when the owningCoredrops; a timer task that observes it releases its pending handle and bails (mirrors the oldWeak::upgrade() == Noneteardown path exactly).runnable— the per-group “this Core has work” wake bit. S2b lands the field + sets/clears it (D227 builds the full mailbox shape now); S4 wires it to wave drain-scoping + finalize, and the host-executor (M6) reads it to schedule the owning worker. The in-wave drain (BatchGuard::drain_and_flush) already gates on it so the no-producer §7 floor pays one atomic load, not a mutex.
§This is ONE mechanism (not six)
The S2b design history records six producer “forks” (D227–D233); that is design-question count, not runtime surface. What actually exists is one owner-side re-entry mechanism:
A
Send + SyncFIFO of deferredMailboxOps, drained on the owner thread — in-wave to quiescence for producer sinks (BatchGuard::drain_and_flush) and at the embedder pump point for autonomous timer tasks (crate::node::Core::drain_mailbox) — each op applied via the one object-safe owner re-entry surfacecrate::node::CoreFull.
MailboxOp keeps a typed fast path (Emit/Complete/Error:
zero-alloc, and a deterministic Core-gone handle-release contract —
see the per-kind post_*) plus a Defer escape hatch (a boxed
FnOnce(&dyn CoreFull) for value-returning topology mutation:
windowing / higher-order inner subscribe). Collapsing the enum to
pure Defer was considered and rejected — it would heap-allocate per
timer/producer emit AND lose the typed Core-gone release affordance
(a dropped FnOnce can’t run an else-branch). ProducerEmitter
(graphrefly-operators) is thin sugar over post_*; the producer
build closure isn’t a mechanism at all — it uses the &Core its
ProducerCtx already lends it (D231). One queue, one drain loop, one
match, one re-entry trait.
Structs§
- Core
Mailbox Send + Syncmailbox bridging autonomous async producers to an owned, relocatablecrate::node::Core. Held behind anArc; theCoreowns one clone, each timer task another. See the module docs.- Defer
Queue - Owner-only deferred-closure queue (D249/S2c — the minimal Defer
split pulled out of
CoreMailbox; D254 (S5) relaxed off cross- thread primitives).
Enums§
- Mailbox
Op - A re-entry request posted to the
CoreMailboxby an autonomous async producer (timer task →Emit) or by a producer-operator sink (D232-AMEND/A′ →Emit/Complete/Error). Applied owner-side via the syncCore::{emit,complete,error}bycrate::node::Core::drain_mailbox— drained in-wave to quiescence by theBatchGuarddrain loop for producer sinks (immediate, cascade-ordering-preserving), and at the embedder pump point for timer tasks (D230).
Type Aliases§
- DeferFn
!Sendowner-side deferred closure (D248/D249/S2c). Posted by an owner-side in-wave producer/graph sink whose closure captures!Sendstate (a relaxedSink/Rc<RefCell<GraphInner>>— D248). Lives in the owner-onlyDeferQueue, never the cross-threadCoreMailbox.- Send
Defer Fn Sendcross-thread deferred closure (D233; D249/S2c). Posted by any cross-threadSendproducer (canonically an autonomous timer task —temporal.rswindow_time/etc., atokio::spawned cross-thread task whose defer closure captures onlySendstate:Arc<Mutex<NodeId>>/Arc<dyn BindingBoundary>/ ids — but the API admits any future cross-thread producer with the sameSendcapture discipline, e.g. a napi/pyo3 binding-layer pump) and applied owner-side. Rides theSend + SyncCoreMailbox(cross-thread post side, drained owner-side viadrain_mailbox).