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
Platform spec feature
Fiber scheduler and stacks
Spec standingStandard
-
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).
What this feature specifies
Cooperative M:N scheduling: many fibers on an OS worker pool, growable stacks (64 KiB initial, 8 MiB cap), and compiler stack maps at safepoints. fiber_* builtins back corelib Fiber<T>; main runs on fiber 0 and shutdown Joins non-Detach children. Phase A documents a single GC mutator; Phase B adds parallel mutators without API break.
Implementation anchors
compiler/crates/beskid_runtime/src/scheduler/— split scheduler state, TLS, spawn/cancel, run loop, and syscall parking modulescompiler/crates/beskid_abi/— symbols andBUILTIN_SPECScompiler/crates/beskid_analysis/src/builtins.rs—__fiber_*injected pathscompiler/crates/beskid_codegen/— safepoints, stack maps
Contract statement
The Beskid runtime provides a cooperative M:N scheduler: many fibers multiplexed on a pool of OS worker threads. Fiber code uses growable stacks and compiler-emitted stack maps at safepoints. The runtime exports fiber_* builtins consumed by Fiber<T> in corelib—no async/await lowering.
Preemptive parallelism for user code uses System.Threading (OS threads), not preempted fibers in v1.
Architecture
flowchart LR subgraph runtime [beskid_runtime] SCH[Scheduler] POOL[OS thread pool] FB[fiber stacks] SCH --> POOL SCH --> FB end subgraph corelib [corelib_concurrency] FT[Fiber T struct] FT -->|fiber_* builtins| SCH end subgraph compiler [beskid_codegen] SP[spawn lowering] SM[stack maps] SP --> SM endPhase A vs Phase B
| Phase | Mutator threads | GC | Fiber scheduler |
|---|---|---|---|
| A (v0.2 target) | One GC mutator; fibers may run on multiple OS threads but GC obeys single-mutator rules | Existing arena; document barriers as no-op until Phase B | Cooperative; park at Send/Receive/syscall |
| B (documented) | Multiple parallel mutators | Concurrent mark/sweep + real gc_write_barrier | Optional function-entry preemption |
Phase B must be specified in memory-and-gc-runtime-contract before implementation; Phase A ships first.
Runtime builtins (normative names)
Illustrative ABI symbols (exact names in beskid_abi/src/symbols.rs):
fiber_spawn,fiber_spawn_with_cancel_slot,fiber_join_status,fiber_join_value,fiber_detach,fiber_cancel,fiber_yieldfiber_current_id(diagnostics),fiber_processor_count(scheduler sizing)- Monotonic clock builtin for
Concurrency.NowMillis()(replacesrt_now_millis)
Deprecated: rt_yield (sched feature) → fiber_yield.
Syscall Parking
M6 completion requires blocking runtime syscalls to enqueue host blocking work on the syscall pool, park only the current fiber, and wake that fiber when the worker finishes. Worker threads must not execute generated Beskid code or perform generated-runtime allocation directly; any runtime object creation happens after the fiber resumes on the scheduler thread.
Main fiber
main() runs on fiber 0. Runtime starts scheduler before main. When main returns, the runtime Joins every spawned fiber that was not Detached, then shuts down the thread pool. Detach fibers are not joined at shutdown; their panic still aborts the process.
Stacks
- Initial stack 64 KiB, growable, cap 8 MiB
- Stack overflow →
FiberError::StackOverflowsurfaced at Join on the affected fiber (no undefined behavior)
Decisions
Section titled “Decisions”No open decisions. Closed choices are normative ADRs under adr/ (D-EXEC-RT-0001 … D-EXEC-RT-0004); use the reader ADRs tab for expandable detail.
Articles
- M:N cooperative fiber schedulerMany fibers multiplex on an OS worker pool without async/await lowering.
- Fiber scheduler and stacks - Design modelScheduler loops, parking, and stack switching implementation expectations.
- Fiber scheduler and stacks - Verification and traceabilityTests and ABI anchors for fiber scheduler work.