Beskid rejects Rust-style async/await state machines at the language level (D-INC-0008). The runtime must provide cooperative concurrency primitives consumed by corelib_concurrency.
Fiber scheduler and stacks - Design model
Platform spec article
Fiber scheduler and stacks - Design model
Missing owner or submitter in frontmatter for this article.
-
Many fibers multiplex on an OS worker pool without async/await lowering.
Context
Decision
Rule Detail Model M:N — many fibers on a pool of OS worker threads API surface fiber_*builtins backFiber<T>; no async lowering in compilerPreemption User fibers are cooperative; preemptive parallelism uses System.Threading(OS threads)Work stealing Per-worker run queues; work-stealing permitted Parking Fibers park on channel ops, join, cancel, and blocking syscalls Consequences
Scheduler modules live under
beskid_runtime/src/scheduler/. Deprecatedrt_yield(schedfeature) is superseded byfiber_yield.Verification anchors
compiler/crates/beskid_runtime/tests/concurrency.rs; corelib concurrency tests; git:12ee673,76a58f5. -
Growable stacks with documented overflow behavior at Join.
Context
Fiber stacks must balance memory use against deep call chains from generated code and corelib. GC needs precise stack maps at safepoints on every fiber stack.
Decision
Parameter Value Initial size 64 KiB per fiber Growth Growable until cap Maximum 8 MiB cap per fiber Overflow FiberError::StackOverflowat Join — no undefined behaviorSwitching Callee stacks ABI-aligned; callee-saved registers saved per platform ABI GC All fiber stacks enumerable at safepoints via compiler stack maps Consequences
Documentation and corelib must cite these limits. Stack switching may use manual swap techniques documented in design model.
Verification anchors
Scheduler stack allocator; fiber spawn/join integration tests.
-
Only one thread holds Beskid mutator role for allocation at a time.
Context
Fibers may run on multiple OS threads while Phase A GC still uses one process-wide arena (
enter_runtime_scope). Syscall pool workers must not become second mutators.Decision
Rule Detail Phase A (default) One thread at a time may execute Beskid allocations as GC mutator Scheduler Transfers mutator execution between fibers on that contract Syscall pool Workers run blocking host work without arbitrary Beskid mutator code or generated allocation; runtime tags them and traps stray allocations ( assert_mutator_allowed)Phase B (opt-in, v0.3) Multiple Beskid mutators may share one heap by holding a MutatorAttachGuardfromattach_phase_b_mutator; pointer-payload channel ops applygc_write_barrieron send and receiveFuture Phase B becomes the default once preemption code emission and full concurrent-mark stress coverage land Aligns with D-CORE-CONC-0007.
Consequences
run_blockingpaths park fibers and resume on scheduler threads for mutator work.Verification anchors
beskid_runtimescheduler +enter_runtime_scope; concurrency runtime tests (tests/concurrency.rs,tests/gc_concurrency.rs); Phase B opt-in coverage intests/phase_b_concurrency.rsexercises multi-mutator allocation, pointer-payload channel write barriers, and the syscall-pool allocation guard. -
main returns after joining non-Detached fibers; Detach panics still abort.
Context
Process exit must not leak running fibers that share the GC heap. Fire-and-forget tasks still need defined failure behavior.
Decision
Rule Detail Main fiber main()runs on fiber 0; scheduler starts before entryNormal exit When mainreturns, runtime Joins every spawned fiber that was not DetachedDetach Detach fibers are not joined at shutdown Detach panic Unjoined Detach child panic still aborts the process Pool shutdown After joins complete, worker thread pool stops Matches D-CORE-CONC-0004.
Consequences
Hosts and tests must account for shutdown latency from outstanding joins.
Verification anchors
Runtime main harness; corelib concurrency shutdown tests.
0 revisions (git unavailable at build; counts may be empty)
No commits recorded for this path.
| Section id | Required | Found |
|---|---|---|
what-this-feature-specifies | yes | yes |
implementation-anchors | yes | yes |
Full tree: run pnpm verify:platform-spec-layout (writes src/generated/platform-spec-layout-report.json).
Scheduler model
Section titled “Scheduler model”- Run queue(s) per worker OS thread; work-stealing permitted.
- Parked fibers live off-run-queue until wake (channel ready, cancel, join completion).
fiber_yield— cooperative requeue of current fiber.- Blocking syscall paths park fiber; blocking work runs on thread pool threads that do not execute arbitrary Beskid mutator code without attaching GC scope (Phase A: mutator attachment rules stay strict).
Stack switching
Section titled “Stack switching”Implementation may use manual stack swap coroutines (see lightweight coroutines in Rust (fibers) for techniques). Requirements:
- Callee stack aligned to platform ABI.
- On switch, save callee-saved registers per ABI.
- GC can enumerate all fiber stacks at safepoint (stack maps required).
Reentrancy (Phase A — closed)
Section titled “Reentrancy (Phase A — closed)”Phase A uses one process-wide GC arena (existing enter_runtime_scope model). OS worker threads in the pool must not execute arbitrary Beskid mutator code concurrently; they run scheduler bookkeeping or blocking syscalls without a second mutator attachment.
Only one thread at a time may hold the mutator role for GC allocation in Phase A. The scheduler transfers mutator execution between fibers on that contract. Phase B revisits parallel mutators under memory-and-gc-runtime-contract.
Processor count
Section titled “Processor count”ProcessorCount / SetProcessorCount map to worker pool size (Go GOMAXPROCS analogue); default logical CPUs.