Authors need predictable queue semantics without implicit blocking on the default channel.
Concurrency package
Platform spec feature
Concurrency package
Spec standingStandard
-
Factory defaults and explicit bounded/unbounded options for Channel construction.
Context
Decision
Rule Detail Default Unbounded when ChannelOptionsis omitted or no bounded capacity is setBounded ChannelOptions.Bounded(n)withn > 0Unbounded ChannelOptions.Unbounded(equivalent to default)Factory Channel<T>.Create(options: ChannelOptions = default)Consequences
Documentation must warn about memory growth on unbounded channels. Bounded queues park senders when full.
Verification anchors
Corelib concurrency tests; runtime channel builtins.
-
Cancellation signalling, event ordering, and join/channel error paths.
Context
Fibers need cooperative cancellation without panics on join or parked channel ops.
Decision
Rule Detail Signal Fiber.Cancel()sets runtime cancellation flagEvent Each Fiber<T>declaresevent OnCancelled(); runtime raises on child before unblocking parked opsJoin Join→Result<T, FiberError::Cancelled>after cancellation observedChannels Parked Send / Receive → ChannelError::CancelledOrdering OnCancelled runs before Join / channel errors; handlers must not block on Join of self Consequences
Unhandled panic in OnCancelled aborts process in v1 (same as other unhandled event paths).
Verification anchors
Corelib + runtime concurrency tests; cancel/join fixtures.
-
Fair multiplexing across registered channels when multiple receives are ready.
Context
FIFO registration order starves late channels when an early channel is always ready.
Decision
Rule Detail Algorithm Round-robin among channels with a ready Receive Cursor Per- Hubindex advanced after each successful WaitReceivev1 scope WaitReceive only — no WaitSend Consequences
Console hubs should keep registration count small (under 16 typical).
Verification anchors
Hub integration tests in runtime and corelib suites.
-
main() fiber id, join/detach policy, and process teardown.
Context
Process lifetime must be defined when main returns while child fibers still run.
Decision
Rule Detail Main main()runs on fiber 0Shutdown When mainreturns, runtime Joins spawned fibers that were not DetachedDetach Detach waives parent Join; child panic still aborts process Leak Spawn without Join or Detach before mainends → conformance warning in v0.2 testsConsequences
Future recovery policies require a new ADR; v1 aborts on undetached child panic.
Verification anchors
Runtime shutdown tests; conformance warnings catalog.
-
Error surfaces for channel, fiber join, and mutex operations.
Context
Panics as control flow for expected failure modes break contracts and LSP stability.
Decision
Surface Rule Channel Send / Receive → Result; TrySend / TryReceive →OptionFiber Join → Result<T, FiberError>; stack overflow →FiberError::StackOverflowat JoinMutex Lock → Result<MutexGuard, MutexError>; TryLock →Option(None= would block)Consequences
v1: Lock may return
Cancelledwhen fiber cancelled—no .NET-style poison.Verification anchors
Corelib API signatures; runtime integration tests.
-
Keyword policy for fiber introduction vs deferred async syntax.
Context
Aligns with inception ADR D-INC-0008; avoids dual concurrency models in v1.
Decision
Rule Detail Keyword spawnrequired for new fibers; nogoalias in v1Reserved asyncandawaitare parse errors (reserved, not implemented)Data transfer Channel only between fibers for data; Mutex / WaitGroup for coordination Handles Fiber<T>andChannel<T>are move-onlyConsequences
Parser and semantic tests reject async/await; spawn lowering returns
Fiber<T>.Verification anchors
Parser fixtures; Fibers and spawn.
-
Ship many fibers with one GC mutator; document phase B parallel mutators.
Context
Parallel GC mutators require write barriers not ready for initial ship.
Decision
Phase Rule A (ship) Many fibers, one GC mutator; gc_write_barrierno-opB (documented) Parallel mutators + real barriers; no corelib API break Consequences
Scheduler and memory specs must stay consistent with phase A barriers.
Verification anchors
Runtime GC tests; memory-and-gc-runtime-contract feature.
-
Processor count, stack sizes, and phase A arena policy.
Context
Hosts need predictable defaults without per-program scheduler tuning.
Decision
Setting Default ProcessorCountHost logical CPU count at init Stacks 64 KiB initial, 8 MiB max Arena Phase A: one process arena; pool threads run Beskid mutator code under scheduler rules Consequences
Fiber scheduler design model article details syscall parking.
Verification anchors
-
Multi-receiver/sender rules and close behavior.
Context
Authors need defined fan-in/fan-out and close idempotence.
Decision
Rule Detail Receivers Multiple allowed; each message delivered to exactly one successful Receive (FIFO) Senders Multiple allowed unless SingleWriter hint (hint only v1) Close Any handle holder may Close; idempotent writer shutdown void spawn Fiber<Unit>when entry returns no valueConsequences
Close after drain returns
ChannelError::ClosedinResult.Verification anchors
Runtime concurrency.rs; corelib channel tests.
-
Hub type parameter, limits, and heterogeneous deferral.
Context
Heterogeneous select requires tagged unions or multiple hubs in v1.
Decision
Rule Detail Fairness Round-robin (see D-CORE-CONC-0003) Max registrations 256 per hub ( HubError::Limitpossible)Element type Homogeneous — one Hub<T>wraps onlyChannel<T>with sameTHeterogeneous Not v1 — use multiple hubs or Channel<HubMessage>Result HubReceiveResult { index: i64, value: T }Consequences
UI/console hubs should stay well below 256 registrations.
Verification anchors
Hub builtin tests; examples article.
-
Spawn entry types vs fiber handle contract surface.
Context
Authors should not declare cancellation events on arbitrary spawn closures.
Decision
Rule Detail Placement OnCancelled on Fiber<T>handle fromspawnonlySpawn entry Ordinary fn(...) -> T; does not declare OnCancelledHandle Fiber<T>struct wrapping runtime builtinsConsequences
Lowering wires cancel slot from handle metadata.
Verification anchors
Semantic + lowering tests for spawn.
-
Mutex API for v1 coordination.
Context
TryLock supports non-blocking attempts; Lock parks with cancel path.
Decision
Rule Detail TryLock In v1 — returns Option<MutexGuard>Lock Parks until acquired or Cancelled Consequences
No poison semantics in v1.
Verification anchors
Mutex corelib tests.
-
Monotonic time API owned by concurrency package in v0.2.
Context
Legacy
rt_now_millisand scattered clock builtins confuse package boundaries.Decision
Concurrency.NowMillis() -> i64in concurrency package replaces legacyrt_now_millis. Wall clock deferred to futureSystem.Time.Consequences
Wall-clock ADR required before exposing civil time in corelib.
Verification anchors
Builtin spec table; corelib clock smoke tests.
-
Compile-time rule preventing join cycles on fiber stacks.
Context
Parent/child Join cycles deadlock the scheduler.
Decision
Child Join parent → compile-time diagnostic JoinWouldDeadlock. Parent Join child and sibling Join remain allowed.
Consequences
Diagnostic registry documents JoinWouldDeadlock.
Verification anchors
Semantic analyzer tests for join graph.
- Concurrency package - Contracts and edge cases Send, Receive, Join, Cancel, and Hub normative contracts.
- Concurrency package - Decisions record (legacy index) Migration index pointing to per-decision ADR files under adr/—normative text lives in ADRs, not this page.
- Concurrency package - Design model Package layout, module map, and thin-wrapper rule for Fiber and Channel structs.
- Concurrency package - Examples Illustrative spawn, Channel, and Hub usage (informative).
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).
Layer diagram
flowchart TB author[Beskid source spawn / Channel / Hub] corelib[corelib_concurrency structs] builtins[runtime builtins fiber_* channel_* hub_*] scheduler[fiber scheduler + GC safepoints] author --> corelib --> builtins --> scheduler
The package is a thin wrapper layer only — no duplicate channel abstractions or async state machines.
What this feature specifies
Normative corelib shapes for cooperative concurrency: Fiber<T>, Channel<T>, homogeneous Hub<T>, Mutex, and WaitGroup as thin wrappers over stable runtime builtins. spawn lowering returns a move-only fiber handle with OnCancelled on the handle; channel and join failures use Result / Option, not panic.
Implementation anchors
- Workspace package
compiler/corelib/packages/concurrency/(corelib_concurrency) - Runtime symbols:
compiler/crates/beskid_abi/src/symbols.rs,BUILTIN_SPECS - Corelib tests:
compiler/corelib/beskid_corelib/tests/corelib_tests/src/concurrency/ - Runtime integration:
compiler/crates/beskid_runtime/tests/concurrency.rspluscompiler/crates/beskid_tests/src/runtime/jit.rsspawn smoke coverage
Contract statement
The corelib_concurrency workspace package provides the only supported user-facing API for cooperative fibers and channel communication. Public types are structs whose methods call stable runtime builtins (fiber_*, channel_*, hub_*, mutex_*, wait_group_*). The package must not introduce secondary abstraction layers (no builder stacks, no implicit async state machines, no duplicated channel interfaces).
All fiber operations that can fail must return Core.Results.Result (or Option where absence is ordinary). Panics are not the error path for channel or join failures.
Inputs and outputs
| Surface | Role |
|---|---|
Fiber<T> | Opaque handle wrapping fiber_* builtins; T is the spawn entry return type |
Channel<T> | Unbounded by default; ChannelOptions.Bounded(n) for bounded queues |
Hub<T> | Homogeneous multichannel WaitReceive (round-robin); unlike types use Channel<HubMessage> or multiple hubs |
Mutex | Mutual exclusion between fibers on shared state |
WaitGroup | Fork–join counter for batches of fibers |
Concurrency.Yield() | Cooperative reschedule (fiber_yield) |
Concurrency.NowMillis() | Monotonic milliseconds (fiber_now_millis / clock builtin) |
Concurrency.Spawn | Used by lowering of spawn; returns Fiber<T> with OnCancelled on handle |
Fiber.Join | Result<T, FiberError> |
Fiber.Detach | Fire-and-forget; unjoined panic still aborts process unless recovered at runtime policy |
Fiber.Cancel | Sets cancel flag; runs child event OnCancelled(); Join → FiberError::Cancelled |
State model
Fiber<T>— runtime handle + metadata (id, cancellation flag). Not copyable unless spec later allows handle duplication; move-only preferred.Channel<T>— holds runtime queue id; Close marks writer end closed; Receive after drain returnsChannelError::ClosedinResult.Hub— registered set of channel endpoints with cached readiness index updated by runtime on send/receive.- Phase A (see scheduler spec): many fibers, single GC mutator thread documented; Phase B adds parallel mutators without API break.
Algorithms and flow
spawn entrylowers tofiber_spawn_with_cancel_slot(entry, env, onCancelledSlot); parent gets aFiber<T>backed by the runtimei64fiber id.- Send — if queue full, park current fiber (not OS thread); on success establishes happens-before edge to receiver.
- Receive — if empty and open, park; if closed and empty, return
Result::Err(Closed). - TrySend / TryReceive — non-blocking;
Optionfor empty/full. - Hub.WaitReceive — runtime waits until any registered channel can satisfy Receive; returns which member completed.
- Blocking syscall paths in
System.Syscallshould park the calling fiber and delegate blocking work to the thread pool (runtime), not stall the whole scheduler.
Edge cases and errors
- Send after Close →
Result::Err(ChannelError::Closed)(no panic). - Cancel on joined fiber →
FiberError::Cancelledon Join. - Detach child panic → process abort after diagnostic unless runtime policy adds domain recovery later.
- Fibers must not share pointers to another fiber’s stack; Channel is the only approved cross-fiber communication (language + memory model).
Compatibility and versioning
New builtins require beskid_runtime_abi_version bump and entries in BUILTIN_SPECS / define_builtins!. Deprecated v0.1 rt_yield / rt_now_millis (sched feature) are superseded by fiber_yield and monotonic clock builtins—remove legacy doc references to async/await.
Security and performance notes
- Default fiber stack: 64 KiB, growable cap 8 MiB (runtime).
- Unbounded channels are opt-in; documentation must warn about memory growth.
- Hub registration count should be bounded in UI scenarios (console) to avoid linear scans on hot paths.
Examples
See examples article: spawn + Join, bounded Channel, Hub for stdin/resize multiplexing sketch.
Verification and traceability
- Corelib compile tests under
compiler/corelib/beskid_corelib/tests/corelib_tests/src/concurrency/ - Runtime integration: canonical primitive coverage in
compiler/crates/beskid_runtime/tests/concurrency.rs; JIT smoke incompiler/crates/beskid_tests/src/runtime/jit.rs - Conformance: bounded/closed Channel, Cancel, Hub readiness ordering, Mutex, WaitGroup, and syscall parking
Decisions
No open decisions. Closed choices are normative ADRs under adr/ (D-CORE-CONC-0001 … D-CORE-CONC-0014); use the reader ADRs tab for expandable detail. Legacy decisions record is a migration index only.
- System.Threading — OS threads (preemptive), not fibers
- Console terminal events — delivers via Channel
Articles
- Channel default capacity is unboundedFactory defaults and explicit bounded/unbounded options for Channel construction.
- Concurrency package - Contracts and edge casesSend, Receive, Join, Cancel, and Hub normative contracts.
- Concurrency package - Decisions record (legacy index)Migration index pointing to per-decision ADR files under adr/—normative text lives in ADRs, not this page.
- Concurrency package - Design modelPackage layout, module map, and thin-wrapper rule for Fiber and Channel structs.
- Concurrency package - ExamplesIllustrative spawn, Channel, and Hub usage (informative).