pub struct DeferQueue { /* private fields */ }Expand description
Owner-only deferred-closure queue (D249/S2c — the minimal Defer
split pulled out of CoreMailbox; D254 (S5) relaxed off cross-
thread primitives).
Under D248 full single-owner the substrate Sink/TopologySink
dropped Send + Sync, so a DeferFn (which captures relaxed
Sinks / Rc<RefCell<GraphInner>>) is !Send. It cannot ride the
Send + Sync CoreMailbox (that bridges the timer.rs
cross-thread Arc<CoreMailbox> post side). This queue is therefore
owner-only and !Send: held behind an Rc shared between
crate::node::Core and graphrefly-operators’ ProducerEmitter
on the one owner thread. Drained owner-side by
crate::node::Core::drain_mailbox (after the id-mailbox) and the
in-wave BatchGuard drain.
Owner-thread-only by construction (D254 (S5), 2026-05-19). The
Rc<DeferQueue> is never sent across threads (the closures it holds
are !Send), so the cross-thread primitives it used to carry —
parking_lot::Mutex<VecDeque<DeferFn>> for q and AtomicBool for
runnable — were unused capacity. D254 collapses them to
RefCell<VecDeque<DeferFn>> + Cell<bool>, dropping the
parking_lot::Mutex::lock acquire on every owner-side post/drain
(the hottest D251 path). closed similarly drops to Cell<bool> —
it is set by Drop for Core on the owner thread (the only close()
call site) and read by post/drain_into on the same thread.
S4 still does the per-group-wake + typed snapshot/prune MailboxOp
reshape (D246 rule 8) — D249 is the acknowledged minimal first
touch that lets the D248 single-owner Sink relaxation land in S2c.
Implementations§
Source§impl DeferQueue
impl DeferQueue
Sourcepub fn is_runnable(&self) -> bool
pub fn is_runnable(&self) -> bool
Whether the queue currently holds work (the in-wave drain gate).
#[must_use] (/qa m4) — a discarded result silently loses the
drain-gate signal.
Sourcepub fn post(&self, f: DeferFn) -> bool
pub fn post(&self, f: DeferFn) -> bool
Enqueue an owner-side deferred closure. Returns false iff the
owning Core has dropped (Self::close) — the caller’s
closure is dropped unrun (same contract as the old
CoreMailbox::post_defer). Owner-thread-only by D248/D249
construction — see the struct doc; no cross-thread TOCTOU
against close is possible because both run on the one owner
thread and never overlap.
#[must_use] (/qa m3) — false is the load-bearing signal that
the caller’s closure was dropped unrun; ignoring it can mask
teardown races where work was silently discarded.
Sourcepub fn drain_into(&self, max_ops: u32, apply: impl FnMut(DeferFn))
pub fn drain_into(&self, max_ops: u32, apply: impl FnMut(DeferFn))
Owner-side drain. Pops every queued closure FIFO and applies it
(re-entrancy-safe: a closure may re-post; a later drain picks
it up). max_ops bounds one drain against a self-re-posting
livelock (mirrors CoreMailbox::drain_into).
Re-entry note (/qa M6). Under the pre-D254
parking_lot::Mutex<VecDeque<DeferFn>> shape, an owner-thread
re-entrant drain_into from inside apply(f) would have
deadlocked at the inner lock acquire. Under the D254
RefCell<VecDeque<DeferFn>> shape it would panic with
BorrowMutError instead — different failure mode, same
fail-loud outcome. In practice the pre-D254 design never had any
in-tree re-entrant drain caller (the embedder pump’s
drain_mailbox is never invoked recursively from within an
apply closure body — drain_mailbox is the embedder seam, not
a DeferFn capture target), so neither failure mode is
reachable from in-tree code. Documented for any future binding
that might construct one.
§Panics
Panics if a single drain applies max_ops closures — a Defer
closure re-posting itself every application (owner-side
livelock), the defer-queue analogue of a fn that emits to itself.
Sourcepub fn close(&self)
pub fn close(&self)
Mark the owning Core gone. Idempotent.
Drop timing of queued closures (QA, 2026-05-19). This method
only flips closed so subsequent post calls drop their
closures unrun (closed == true short-circuits enqueue) and
running CoreFull on a half-dropped Core is structurally
impossible. The already-queued closures are NOT drained here;
they remain in q and are dropped when the last Rc<DeferQueue>
clone drops (every ProducerEmitter / graph reactive handle
holds one — D249). Since closures by contract capture no
HandleId (D235 P8 / D246 r8 sites), this is leak-free for
refcount purposes; callers MUST honour the no-HandleId-in-
DeferFn discipline (see DeferFn’s doc) for this to hold.