Skip to main content

Module mailbox

Module mailbox 

Source
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::emitno 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 owning Core drops; a timer task that observes it releases its pending handle and bails (mirrors the old Weak::upgrade() == None teardown 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 + Sync FIFO of deferred MailboxOps, 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 surface crate::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§

CoreMailbox
Send + Sync mailbox bridging autonomous async producers to an owned, relocatable crate::node::Core. Held behind an Arc; the Core owns one clone, each timer task another. See the module docs.
DeferQueue
Owner-only deferred-closure queue (D249/S2c — the minimal Defer split pulled out of CoreMailbox; D254 (S5) relaxed off cross- thread primitives).

Enums§

MailboxOp
A re-entry request posted to the CoreMailbox by an autonomous async producer (timer task → Emit) or by a producer-operator sink (D232-AMEND/A′ → Emit/Complete/Error). Applied owner-side via the sync Core::{emit,complete,error} by crate::node::Core::drain_mailbox — drained in-wave to quiescence by the BatchGuard drain loop for producer sinks (immediate, cascade-ordering-preserving), and at the embedder pump point for timer tasks (D230).

Type Aliases§

DeferFn
!Send owner-side deferred closure (D248/D249/S2c). Posted by an owner-side in-wave producer/graph sink whose closure captures !Send state (a relaxed Sink / Rc<RefCell<GraphInner>> — D248). Lives in the owner-only DeferQueue, never the cross-thread CoreMailbox.
SendDeferFn
Send cross-thread deferred closure (D233; D249/S2c). Posted by any cross-thread Send producer (canonically an autonomous timer task — temporal.rs window_time/etc., a tokio::spawned cross-thread task whose defer closure captures only Send state: Arc<Mutex<NodeId>> / Arc<dyn BindingBoundary> / ids — but the API admits any future cross-thread producer with the same Send capture discipline, e.g. a napi/pyo3 binding-layer pump) and applied owner-side. Rides the Send + Sync CoreMailbox (cross-thread post side, drained owner-side via drain_mailbox).