Skip to main content

ResumeDriver

Struct ResumeDriver 

Source
pub struct ResumeDriver;
Expand description

Drives CompiledProcess::resume() calls (scheduler.md §5): start (SC-059), resume_for (SC-060/SC-061), and fork/join orchestration (handle_fork/on_child_done, Resolved design decision D1). See this module’s doc comment for scope and the SC-061 fix resume_for embodies.

Implementations§

Source§

impl ResumeDriver

Source

pub fn resume_for( &self, table: &ProcessTable, proc: ProcessId, arg: ResumeArg, state: SimHandle, ops: &dyn SchedulerOps, ) -> Result<Suspend, ResumeError>

SC-061: “the scheduler must never call resume() concurrently on the same ProcessId; one process activation is the atomicity unit.” See this module’s doc comment for the fix this method embodies.

Source

pub fn start( &self, table: &ProcessTable, proc: ProcessId, state: SimHandle, ops: &dyn SchedulerOps, ) -> Result<Suspend, ResumeError>

SC-059: “the scheduler must call resume(Start) exactly once to begin each registered process.” The once-only guarantee is ProcessDescriptor::started’s AtomicBool CAS: a losing compare_exchange (already true) is rejected with Err(ResumeError::AlreadyStarted(proc)) before compiled’s lock is even acquired, so a second start call never reaches resume() at all — never a panic, matching resume_for’s typed-error style for the same reason (misuse rejection, not exhaustive-match fallback, but the same no-silent-no-op posture).

Source

pub fn handle_fork( &self, table: &mut ProcessTable, parent: ProcessId, kind: JoinKind, spawns: &[SpawnSpec], mint_child: &mut dyn FnMut(&SpawnSpec) -> Box<dyn CompiledProcess>, ) -> Vec<ProcessId> ⓘ

Fork/join per Resolved design decision D1 (docs/CONTRACTS.md), using D1a’s v1 sched-internal shadow bookkeeping on ProcessDescriptor (fork_join_kind/fork_remaining/fork_any_signaled, plain fields — not process::ForkFrame, which sched does not own/construct this wave). Registers one child per SpawnSpec via ProcessTable::register (mint_child stands in for “codegen emits each branch as its own process-table kernel”), writes each new child’s own ProcessDescriptor::fork_parent = Some(parent) (LRM 9.3.2 — this is handle_fork’s one and only writer of fork_parent), and sets the parent’s fork_children/fork_join_kind/fork_remaining/ fork_any_signaled per kind (All => children.len(), Any => 1.min(children.len()), None => 0 — None leaves fork_remaining at 0, the caller-visible “parent proceeds now, no on_child_done needed” signal). table: &mut ProcessTable (not the shared &ProcessTable resume_for/start take) is correct here: this wave never calls handle_fork concurrently with anything else (see this module’s doc comment).

Source

pub fn on_child_done( &self, table: &mut ProcessTable, child: ProcessId, ) -> Result<Option<ResumeArg>, ResumeError>

D1: reports one fork child’s completion to its parent, returning the parent’s own next resume() argument exactly when the join condition is satisfied — None otherwise (parent not resumed yet, or the parent’s join was already satisfied and this is a later, detached child under JoinKind::Any). Looks up child’s parent via the child’s own ProcessDescriptor::fork_parent (an O(1) read set by handle_fork above), never a linear scan over every parent’s fork_children.

Task 9 (B-sched wave, SC-125) additions: before the ordinary fork_join_kind (All/Any/None) computation below, this method now also checks child’s parent for an active wait_order (ProcessDescriptor::wait_order, installed by Engine:: apply_wait_order) or wait_fork (wait_fork_remaining, installed by Engine::apply_wait_fork) wait — both genuinely independent of fork_join_kind in practice (a real wait fork/wait_order is always preceded by a fork ... join_none, which leaves fork_join_kind permanently at Some(JoinKind::None), itself always a no-op arm below — see handle_fork’s own doc comment). Return type changed from Option<ResumeArg> to Result<Option<ResumeArg>, ResumeError> to carry ResumeError::WaitOrderViolated — every pre-existing call site (this file’s own tests, sched::control::Engine::apply_suspend) is updated accordingly; the ordinary All/Any/None paths still only ever produce Ok(..), never Err.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V