pub enum Suspend {
Delay(u64),
WaitEvents(EventWaitSet),
ForkJoin(JoinKind, Vec<SpawnSpec>),
WaitFork,
WaitOrder(Vec<ProcessId>),
WaitForGvt,
ByRequest,
Done,
}Expand description
What a CompiledProcess::resume() call returns to the in-process scheduler
orchestration loop. See crate::process::WaitSpec for the lower-level,
codegen/ABI-facing sibling of this type and the reconciliation note there.
Fork/join (RESOLVED, docs/CONTRACTS.md “Resolved design decisions” D1).
A fork hands the scheduler spawn descriptors, not process identities and
not process objects. The three design docs disagreed three ways
(scheduler.md: Vec<Box<dyn CompiledProcess>>; the r2 placeholder:
Vec<ProcessId>; process.md §7: an undefined ProcessHandle):
Vec<ProcessId>is wrong: the children do not exist yet when the parent suspends, so nothing could have assigned those ids.Box<dyn CompiledProcess>contradicts ARCHITECTURE §5’s frozen ABI (a process is anextern "C"ProcessFnentry inVJ_PROCESS_TABLE, not a Rust trait object) and cannot satisfySuspend: Clone.
Fork branches are statically known code: codegen emits each branch as its
own process-table kernel. So the parent returns one SpawnSpec per
branch; the scheduler registers each, assigns the child its ProcessId,
records the ids in the parent’s ForkFrame::children, sets
ForkFrame::remaining per JoinKind (All = n, Any = 1, None = 0 — the
parent resumes immediately), and later reports each completion back via
ResumeArg::ForkChildDone { child }. Children reach variables declared in
the parent’s enclosing scopes through SpawnSpec::parent (LRM 9.3.2).
Rolling back a parent that has already spawned live children must also
roll back / kill those children — owned by the determinism rows, not this
type.
Variants§
Delay(u64)
WaitEvents(EventWaitSet)
ForkJoin(JoinKind, Vec<SpawnSpec>)
WaitFork
NEW (Task 9, B-sched wave, ledger SC-125) — scheduler-side half of
wait fork;. Blocks the calling process until every ProcessId
currently live in its own ProcessDescriptor::fork_children
(populated by an earlier fork ... join_none) has reached
Suspend::Done and been reclaimed; resumes via
ResumeArg::AllForkChildrenDone. See sched::control::Engine:: apply_wait_fork and sched::resume::ResumeDriver::on_child_done’s
own doc comments for the mechanism. disable fork; is deliberately
not a Suspend variant here – it never suspends the caller, so
it is instead a synchronous SchedulerOps::disable_fork call (see
that method’s own doc comment).
WaitOrder(Vec<ProcessId>)
NEW (Task 9, SC-125) — scheduler-side half of wait_order(a, b, c);. Blocks the calling process until every ProcessId in
order has completed, in exactly that declared order; resumes via
ResumeArg::WaitOrderSatisfied on success. An out-of-order
completion never silently satisfies the wait or panics – it is
surfaced as sched::resume::ResumeError::WaitOrderViolated (see
that type’s own doc comment).
WaitForGvt
Task 8 (AB-085): a barrier-wait suspend – the process yields until
every parallel lane has reached the same GVT boundary. Deliberately
carries no payload and needs no new coroutine/stack mechanism: it
reuses the exact same suspend/resume dispatch every other Suspend
variant goes through (apply_suspend in sched::control handles it
by delegating to the Delay(0) arm’s existing code path – see that
function’s own doc comment). The real GVT-barrier semantics (actually
gating resumption on every parallel lane’s progress, not just
resolving at the next delta step) belong to the determinism/parallel-
engine track and are not implemented by this wave; this variant only
wires the ABI-level suspend reason into the existing dispatch so a
compiled process can report it.
ByRequest
Classes plan Task 13 (LRM 9.7): the process yields because it just
queued a ProcessRequest through SchedulerOps::process_request
(p.await() / p.suspend() called as a statement). Unlike every
other variant it carries no wake condition of its own: the engine
resumes the process immediately unless the request left it
self-suspended or awaiting another process (see
sched::control::Engine::apply_by_request).