Concurrency package
Normative corelib shapes for Fiber, Channel, Hub, Mutex, and WaitGroup—thin builtins wrappers only.
Document layout: Feature capability layoutconforms
## Purpose## Requirements
Concurrency package Specification
Purpose
Normative corelib shapes for Fiber, Channel, Hub, Mutex, and WaitGroup—thin builtins wrappers only.
Requirements
Requirement: Channel default capacity is unbounded: Decision [D-CORE-CONC-0001]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
Rule Detail Default Unbounded when ChannelOptionsis omitted or no bounded capacity is setBounded ChannelOptions.Bounded(n)with`n > 0`Unbounded ChannelOptions.Unbounded(equivalent to default)Factory `Channel<T>.Create(options: ChannelOptions = default)`
Stable ID: BSP-REQ-97154AB51A83
Legacy source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0001-channel-default-unbounded/content.md
Source SHA-256: 83650205eeec69e4bd2e46dce5e8a2aa580085fb8698fb2bf6255ab560ded3d7
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: Fiber cancellation via Cancel and OnCancelled: Decision [D-CORE-CONC-0002]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
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
Stable ID: BSP-REQ-A5E85E461CB0
Legacy source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0002-fiber-cancellation/content.md
Source SHA-256: 32439c4ee437df40a4a470c65b96a63c66c097c84ea172061b1a9517823d9b40
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: Hub WaitReceive uses round-robin fairness: Decision [D-CORE-CONC-0003]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
Rule Detail Algorithm Round-robin among channels with a ready Receive Cursor Per- Hubindex advanced after each successful WaitReceivev1 scope WaitReceive only — no WaitSend
Stable ID: BSP-REQ-0FC9ED4EEC06
Legacy source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0003-hub-round-robin-fairness/content.md
Source SHA-256: a7cc9d1031dd8927e89d27824001a99aaca451785958f9b90a666d8c965dae1f
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: Main fiber and process shutdown: Decision [D-CORE-CONC-0004]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
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 tests
Stable ID: BSP-REQ-2A7B6B98D32F
Legacy source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0004-main-fiber-shutdown/content.md
Source SHA-256: e1e2ed89de65ee2ebde87d898773dedd70defbb80db66f862f5555b3a08966ec
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: Channel and fiber errors use Result not panic: Decision [D-CORE-CONC-0005]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
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)
Stable ID: BSP-REQ-4DA3BD50C58C
Legacy source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0005-result-not-panic-errors/content.md
Source SHA-256: c519a7dc53ee7da0c7abbae040c802d45d09befe04f70221eb61acd0ffe8369c
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: spawn keyword; async and await reserved: Decision [D-CORE-CONC-0006]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
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>`and`Channel<T>`are move-only
Stable ID: BSP-REQ-1B389240A570
Legacy source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0006-spawn-keyword-reserved-async/content.md
Source SHA-256: 6748c306bb82793635aa4306fd9c3f71bff5e8deffaf896c918d22cee075baf9
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: GC phase A single mutator: Decision [D-CORE-CONC-0007]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
Phase Rule A (ship) Many fibers, one GC mutator; gc_write_barrierno-opB (documented) Parallel mutators + real barriers; no corelib API break
Stable ID: BSP-REQ-940953D8F302
Legacy source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0007-gc-phase-a-single-mutator/content.md
Source SHA-256: 56192987dc3b339b6375b91208f7dc2fde7a3803e93e31919b5208e823a3646f
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: Scheduler and stack defaults: Decision [D-CORE-CONC-0008]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
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
Stable ID: BSP-REQ-765789E63BF5
Legacy source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0008-scheduler-defaults/content.md
Source SHA-256: 86c47eab05ec76895b7cc0bae9b2e31fd39b5fe444369dc7dff190f92bca685c
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: Channel runtime delivery semantics: Decision [D-CORE-CONC-0009]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
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 value
Stable ID: BSP-REQ-FD5C40D623FB
Legacy source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0009-channel-runtime-semantics/content.md
Source SHA-256: 4d373548f5291c0ef9f929984fe31d8ac0ada82601d496c66b2f1022d80152dd
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: Homogeneous Hub in v1: Decision [D-CORE-CONC-0010]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
Rule Detail Fairness Round-robin (see D-CORE-CONC-0003) Max registrations 256 per hub ( HubError::Limitpossible)Element type Homogeneous — one `Hub<T>`wraps only`Channel<T>`with sameTHeterogeneous Not v1 — use multiple hubs or `Channel<HubMessage>`Result HubReceiveResult { index: i64, value: T }
Stable ID: BSP-REQ-8CA09EA18363
Legacy source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0010-hub-homogeneous-v1/content.md
Source SHA-256: 60e701c5e01d652eedb6ecc52beb1af33fe9ad93c15295302f1dd490bdee8f1c
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: OnCancelled on Fiber handle only: Decision [D-CORE-CONC-0011]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
Rule Detail Placement OnCancelled on `Fiber<T>`handle fromspawnonlySpawn entry Ordinary `fn(...) -> T`; does not declare OnCancelledHandle `Fiber<T>`struct wrapping runtime builtins
Stable ID: BSP-REQ-CB858CCC189B
Legacy source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0011-fiber-handle-oncancelled/content.md
Source SHA-256: 9348e1a6c85af0ba99c5f85667d2917f5c05d8807f3c690f6a8763d6e57d71a8
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: Mutex TryLock and Lock cancellation: Decision [D-CORE-CONC-0012]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
Rule Detail TryLock In v1 — returns `Option<MutexGuard>`Lock Parks until acquired or Cancelled
Stable ID: BSP-REQ-A8A7B382442B
Legacy source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0012-mutex-trylock-v1/content.md
Source SHA-256: e8e4103dc139d589f7e5a7c6ad0f9da65e8929e6267bc6086e923829ae035f7a
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: Concurrency.NowMillis monotonic clock: Decision [D-CORE-CONC-0013]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
`Concurrency.NowMillis() -> i64`in concurrency package replaces legacyrt_now_millis. Wall clock deferred to futureCore.Time.
Stable ID: BSP-REQ-5D64C12355DB
Legacy source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0013-monotonic-clock/content.md
Source SHA-256: 3dd4187e8c46c71515609a6ecf1251f74023c60f9d85a0c431382b111e8de45d
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Requirement: Forbidden child Join of ancestor: Decision [D-CORE-CONC-0014]
The Beskid standard SHALL enforce the following migrated contract section. Accepted ADR decisions are binding; uppercase requirement keywords retain their BCP-14 meaning.
Child Join parent → compile-time diagnostic JoinWouldDeadlock. Parent Join child and sibling Join remain allowed.
Stable ID: BSP-REQ-6ACA0426EB98
Legacy source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0014-join-ancestor-forbidden/content.md
Source SHA-256: c872474f63ff8d06bfc134fd10fa1feb5287cac369de9f475e071b466529c8df
Scenario: Conformance exercises Decision
- GIVEN an implementation claims conformance with this capability
- WHEN behavior governed by this contract section is exercised
- THEN every MUST, SHALL, REQUIRED, prohibition, and accepted decision in the section is satisfied
Informative Source Provenance
The records below preserve migration history and are not normative except where text was extracted into a requirement above.
Source Record: Concurrency package
Authority: informative provenance
Legacy path: /platform-spec/core-library/concurrency/concurrency-package/
Source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/content.md
SHA-256: 8f86d810bf35a2980a19654e3ff92f9e2f482ccde49c60b0cd82c9e00834980d
Migrated source text
<SpecSection title="Layer diagram" id="layer-diagram">
```mermaid
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.
</SpecSection>
<SpecSection title="What this feature specifies" id="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.
</SpecSection>
<SpecSection title="Implementation anchors" id="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.rs` plus `compiler/crates/beskid_tests/src/runtime/jit.rs` spawn smoke coverage
</SpecSection>
<SpecSection title="Contract statement" id="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.
</SpecSection>
<SpecSection title="Inputs and outputs" id="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` |
</SpecSection>
<SpecSection title="State model" id="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 returns `ChannelError::Closed` in `Result`.
- `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.
</SpecSection>
<SpecSection title="Algorithms and flow" id="algorithms-and-flow">
1. `spawn entry` lowers to `fiber_spawn_with_cancel_slot(entry, env, onCancelledSlot)`; parent gets a `Fiber<T>` backed by the runtime `i64` fiber id.
2. **Send** — if queue full, **park current fiber** (not OS thread); on success establishes happens-before edge to receiver.
3. **Receive** — if empty and open, park; if closed and empty, return `Result::Err(Closed)`.
4. **TrySend** / **TryReceive** — non-blocking; `Option` for empty/full.
5. **Hub.WaitReceive** — runtime waits until any registered channel can satisfy Receive; returns which member completed.
6. Blocking **syscall** paths in `Core.Syscall` **should** park the calling fiber and delegate blocking work to the thread pool (runtime), not stall the whole scheduler.
</SpecSection>
<SpecSection title="Edge cases and errors" id="edge-cases-and-errors">
- **Send** after **Close** → `Result::Err(ChannelError::Closed)` (no panic).
- **Cancel** on joined fiber → `FiberError::Cancelled` on **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).
</SpecSection>
<SpecSection title="Compatibility and versioning" id="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.
</SpecSection>
<SpecSection title="Security and performance notes" id="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.
</SpecSection>
<SpecSection title="Examples" id="examples">
See **[examples](./examples/)** article: spawn + **Join**, bounded **Channel**, **Hub** for stdin/resize multiplexing sketch.
</SpecSection>
<SpecSection title="Verification and traceability" id="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 in `compiler/crates/beskid_tests/src/runtime/jit.rs`
- Conformance: bounded/closed **Channel**, **Cancel**, **Hub** readiness ordering, **Mutex**, **WaitGroup**, and syscall parking
</SpecSection>
<SpecSection title="Decisions" id="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](./decisions-record/) is a migration index only.
</SpecSection>
<SpecSection title="Related features" id="related-features">
- **[Core.Threading](/platform-spec/core-library/concurrency/core-threading/)** — OS threads (preemptive), not fibers
- **[Console terminal events](/platform-spec/core-library/terminal-and-console/console-terminal-events/)** — delivers via **Channel**
</SpecSection>
## Decisions
<!-- spec:generate:adr-index -->
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.
<!-- /spec:generate:adr-index -->
## Articles
<!-- spec:generate:article-index -->
- [Concurrency package - Contracts and edge cases](./articles/contracts-and-edge-cases/)
- [Concurrency package - Decisions record (legacy index)](./articles/decisions-record/)
- [Concurrency package - Design model](./articles/design-model/)
- [Concurrency package - Examples](./articles/examples/)
<!-- /spec:generate:article-index -->
Source Record: Channel default capacity is unbounded
Authority: informative provenance
Legacy path: /platform-spec/core-library/concurrency/concurrency-package/adr/0001-channel-default-unbounded/
Source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0001-channel-default-unbounded/content.md
SHA-256: 83650205eeec69e4bd2e46dce5e8a2aa580085fb8698fb2bf6255ab560ded3d7
Migrated source text
## Context
Authors need predictable queue semantics without implicit blocking on the default channel.
## Decision
| Rule | Detail |
| --- | --- |
| Default | **Unbounded** when `ChannelOptions` is omitted or no bounded capacity is set |
| Bounded | `ChannelOptions.Bounded(n)` with `` `n > 0` `` |
| Unbounded | `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.
Source Record: Fiber cancellation via Cancel and OnCancelled
Authority: informative provenance
Legacy path: /platform-spec/core-library/concurrency/concurrency-package/adr/0002-fiber-cancellation/
Source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0002-fiber-cancellation/content.md
SHA-256: 32439c4ee437df40a4a470c65b96a63c66c097c84ea172061b1a9517823d9b40
Migrated source text
## Context
Fibers need cooperative cancellation without panics on join or parked channel ops.
## Decision
| Rule | Detail |
| --- | --- |
| Signal | `Fiber.Cancel()` sets runtime cancellation flag |
| Event | Each `` `Fiber<T>` `` declares ``event OnCancelled()``; runtime raises on **child** before unblocking parked ops |
| Join | ``Join`` → `` `Result<T, FiberError::Cancelled>` `` after cancellation observed |
| Channels | Parked **Send** / **Receive** → `ChannelError::Cancelled` |
| Ordering | **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.
Source Record: Hub WaitReceive uses round-robin fairness
Authority: informative provenance
Legacy path: /platform-spec/core-library/concurrency/concurrency-package/adr/0003-hub-round-robin-fairness/
Source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0003-hub-round-robin-fairness/content.md
SHA-256: a7cc9d1031dd8927e89d27824001a99aaca451785958f9b90a666d8c965dae1f
Migrated source text
## 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-`Hub` index advanced after each successful **WaitReceive** |
| v1 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.
Source Record: Main fiber and process shutdown
Authority: informative provenance
Legacy path: /platform-spec/core-library/concurrency/concurrency-package/adr/0004-main-fiber-shutdown/
Source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0004-main-fiber-shutdown/content.md
SHA-256: e1e2ed89de65ee2ebde87d898773dedd70defbb80db66f862f5555b3a08966ec
Migrated source text
## Context
Process lifetime must be defined when main returns while child fibers still run.
## Decision
| Rule | Detail |
| --- | --- |
| Main | `main()` runs on fiber **0** |
| Shutdown | When `main` returns, runtime **Join**s spawned fibers that were **not** **Detach**ed |
| Detach | **Detach** waives parent **Join**; child panic still **aborts process** |
| Leak | Spawn without **Join** or **Detach** before `main` ends → conformance **warning** in v0.2 tests |
## Consequences
Future recovery policies require a new ADR; v1 aborts on undetached child panic.
## Verification anchors
Runtime shutdown tests; conformance warnings catalog.
Source Record: Channel and fiber errors use Result not panic
Authority: informative provenance
Legacy path: /platform-spec/core-library/concurrency/concurrency-package/adr/0005-result-not-panic-errors/
Source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0005-result-not-panic-errors/content.md
SHA-256: c519a7dc53ee7da0c7abbae040c802d45d09befe04f70221eb61acd0ffe8369c
Migrated source text
## Context
Panics as control flow for expected failure modes break contracts and LSP stability.
## Decision
| Surface | Rule |
| --- | --- |
| Channel | **Send** / **Receive** → `Result`; **TrySend** / **TryReceive** → `Option` |
| Fiber | **Join** → `` `Result<T, FiberError>` ``; stack overflow → ``FiberError::StackOverflow`` at **Join** |
| Mutex | **Lock** → `` `Result<MutexGuard, MutexError>` ``; **TryLock** → ``Option`` (``None`` = would block) |
## Consequences
v1: **Lock** may return `Cancelled` when fiber cancelled—no .NET-style poison.
## Verification anchors
Corelib API signatures; runtime integration tests.
Source Record: spawn keyword; async and await reserved
Authority: informative provenance
Legacy path: /platform-spec/core-library/concurrency/concurrency-package/adr/0006-spawn-keyword-reserved-async/
Source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0006-spawn-keyword-reserved-async/content.md
SHA-256: 6748c306bb82793635aa4306fd9c3f71bff5e8deffaf896c918d22cee075baf9
Migrated source text
## Context
Aligns with inception ADR D-INC-0008; avoids dual concurrency models in v1.
## Decision
| Rule | Detail |
| --- | --- |
| Keyword | `spawn` required for new fibers; no `go` alias in v1 |
| Reserved | `async` and `await` are **parse errors** (reserved, not implemented) |
| Data transfer | **Channel** only between fibers for data; **Mutex** / **WaitGroup** for coordination |
| Handles | `` `Fiber<T>` `` and `` `Channel<T>` `` are **move-only** |
## Consequences
Parser and semantic tests reject async/await; spawn lowering returns `` `Fiber<T>` ``.
## Verification anchors
Parser fixtures; [Fibers and spawn](/platform-spec/language-meta/evaluation/fibers-and-spawn/).
Source Record: GC phase A single mutator
Authority: informative provenance
Legacy path: /platform-spec/core-library/concurrency/concurrency-package/adr/0007-gc-phase-a-single-mutator/
Source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0007-gc-phase-a-single-mutator/content.md
SHA-256: 56192987dc3b339b6375b91208f7dc2fde7a3803e93e31919b5208e823a3646f
Migrated source text
## Context
Parallel GC mutators require write barriers not ready for initial ship.
## Decision
| Phase | Rule |
| --- | --- |
| A (ship) | Many fibers, **one GC mutator**; `gc_write_barrier` no-op |
| B (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.
Source Record: Scheduler and stack defaults
Authority: informative provenance
Legacy path: /platform-spec/core-library/concurrency/concurrency-package/adr/0008-scheduler-defaults/
Source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0008-scheduler-defaults/content.md
SHA-256: 86c47eab05ec76895b7cc0bae9b2e31fd39b5fe444369dc7dff190f92bca685c
Migrated source text
## Context
Hosts need predictable defaults without per-program scheduler tuning.
## Decision
| Setting | Default |
| --- | --- |
| `ProcessorCount` | Host 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
[Fiber scheduler and stacks](/platform-spec/execution/runtime/fiber-scheduler-and-stacks/).
Source Record: Channel runtime delivery semantics
Authority: informative provenance
Legacy path: /platform-spec/core-library/concurrency/concurrency-package/adr/0009-channel-runtime-semantics/
Source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0009-channel-runtime-semantics/content.md
SHA-256: 4d373548f5291c0ef9f929984fe31d8ac0ada82601d496c66b2f1022d80152dd
Migrated source text
## 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 value |
## Consequences
Close after drain returns `ChannelError::Closed` in `Result`.
## Verification anchors
Runtime concurrency.rs; corelib channel tests.
Source Record: Homogeneous Hub in v1
Authority: informative provenance
Legacy path: /platform-spec/core-library/concurrency/concurrency-package/adr/0010-hub-homogeneous-v1/
Source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0010-hub-homogeneous-v1/content.md
SHA-256: 60e701c5e01d652eedb6ecc52beb1af33fe9ad93c15295302f1dd490bdee8f1c
Migrated source text
## 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::Limit` possible) |
| Element type | **Homogeneous** — one `` `Hub<T>` `` wraps only `` `Channel<T>` `` with same ``T`` |
| Heterogeneous | **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.
Source Record: OnCancelled on Fiber handle only
Authority: informative provenance
Legacy path: /platform-spec/core-library/concurrency/concurrency-package/adr/0011-fiber-handle-oncancelled/
Source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0011-fiber-handle-oncancelled/content.md
SHA-256: 9348e1a6c85af0ba99c5f85667d2917f5c05d8807f3c690f6a8763d6e57d71a8
Migrated source text
## Context
Authors should not declare cancellation events on arbitrary spawn closures.
## Decision
| Rule | Detail |
| --- | --- |
| Placement | **OnCancelled** on `` `Fiber<T>` `` handle from ``spawn`` only |
| Spawn entry | Ordinary `` `fn(...) -> T` ``; **does not** declare **OnCancelled** |
| Handle | `` `Fiber<T>` `` struct wrapping runtime builtins |
## Consequences
Lowering wires cancel slot from handle metadata.
## Verification anchors
Semantic + lowering tests for spawn.
Source Record: Mutex TryLock and Lock cancellation
Authority: informative provenance
Legacy path: /platform-spec/core-library/concurrency/concurrency-package/adr/0012-mutex-trylock-v1/
Source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0012-mutex-trylock-v1/content.md
SHA-256: e8e4103dc139d589f7e5a7c6ad0f9da65e8929e6267bc6086e923829ae035f7a
Migrated source text
## 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.
Source Record: Concurrency.NowMillis monotonic clock
Authority: informative provenance
Legacy path: /platform-spec/core-library/concurrency/concurrency-package/adr/0013-monotonic-clock/
Source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0013-monotonic-clock/content.md
SHA-256: 3dd4187e8c46c71515609a6ecf1251f74023c60f9d85a0c431382b111e8de45d
Migrated source text
## Context
Legacy `rt_now_millis` and scattered clock builtins confuse package boundaries.
## Decision
`` `Concurrency.NowMillis() -> i64` `` in concurrency package replaces legacy `rt_now_millis`. Wall clock deferred to future `Core.Time`.
## Consequences
Wall-clock ADR required before exposing civil time in corelib.
## Verification anchors
Builtin spec table; corelib clock smoke tests.
Source Record: Forbidden child Join of ancestor
Authority: informative provenance
Legacy path: /platform-spec/core-library/concurrency/concurrency-package/adr/0014-join-ancestor-forbidden/
Source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/adr/0014-join-ancestor-forbidden/content.md
SHA-256: c872474f63ff8d06bfc134fd10fa1feb5287cac369de9f475e071b466529c8df
Migrated source text
## 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.
Source Record: Concurrency package - Contracts and edge cases
Authority: informative provenance
Legacy path: /platform-spec/core-library/concurrency/concurrency-package/articles/contracts-and-edge-cases/
Source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/articles/contracts-and-edge-cases/content.md
SHA-256: 2885a2a14616177318d50a84012e07eeff5465e998d1a2db0229b65372dfb0d4
Migrated source text
## `Channel<T>`
### Construction
- `Channel<T>.Create(options: ChannelOptions = default)` — **default is unbounded**.
- `ChannelOptions.Bounded(n)` — `n > 0`; **Send** parks when full (Wait mode).
- `ChannelOptions.Unbounded` — equivalent to default; queue grows until OOM (documented risk).
- `ChannelOptions.SingleReader()` / `SingleWriter()` — unbounded factories that set optimization hints without changing queue semantics.
- Optional capacity field inside options **must not** contradict `Bounded` / `Unbounded` variant; bounded wins when `Bounded(n)` is chosen.
### Send and Receive (full names required)
| Method | Contract |
| --- | --- |
| **Send** | `Result<(), ChannelError>`; blocks/parks fiber when full (bounded + Wait mode) |
| **Receive** | `Result<T, ChannelError>`; parks when empty and open |
| **TrySend** | `Option<()>` — `None` if would block |
| **TryReceive** | `Option<T>` — `None` if would block |
| **Close** | Idempotent writer close; no panic |
### ChannelError (closed set)
- `Closed` — endpoint closed
- `Cancelled` — owning fiber cancelled (**Cancel** / **OnCancelled** path)
**TrySend** / **TryReceive** use `Option` only (`None` = would block); no `ChannelError::Full`.
### ChannelOptions (.NET-inspired, sync-only)
- **SingleReader** / **SingleWriter** — optimization hints; behavior unchanged if violated but may enable faster paths
- **BoundedChannelFullMode::Wait** — default; sender parks
- No `AllowSynchronousContinuations` (async-only .NET concept)
## `Fiber<T>`
`Fiber<T>` is a corelib **struct** (runtime handle) that **must** declare:
```beskid
event OnCancelled();
```
The **spawn entry** function does **not** declare **OnCancelled** — only the handle type does.
| Method | Contract |
| --- | --- |
| **Join** | `Result<T, FiberError>` — `Ok(value)`, `Cancelled`, `StackOverflow`, `Panicked` |
| **Detach** | void; parent waives **Join**; child panic aborts process |
| **Cancel** | void; raises **OnCancelled** on child fiber, then unblocks parked ops with **Cancelled** |
**OnCancelled** runs on the child fiber. Unhandled failure in a handler **aborts the process**. Handlers **must not** **Join** self, **Join** an ancestor, or block indefinitely.
**Join** from a child fiber to a parent (or ancestor) handle is a **compile error** (`JoinWouldDeadlock`).
M3 completion requires generated code to import the ABI symbol `fiber_spawn_with_cancel_slot`, pass the handle's **OnCancelled** event slot, and treat the returned handle as an **`i64` fiber id** rather than a pointer.
### FiberError (closed set)
- `Cancelled`
- `StackOverflow`
- `Panicked` — carries diagnostic handle / message policy per runtime
## `Hub<T>`
- **Homogeneous only in v1** — `Hub<T>` registers only `Channel<T>` instances
- **Register(index, channel)** / **Unregister(index)** — `index` is user-chosen `i64` key in **WaitReceive** result
- **WaitReceive** → `Result<HubReceiveResult, HubError>` where `HubReceiveResult` carries member index and received value
- **Round-robin** fairness among ready channels (see **[decisions record](./decisions-record/)**)
- Multiplexing unlike types: use `Channel<HubMessage>` (enum/union) or multiple hubs — **not** mixed-type **Hub** in v1
- **WaitSend** — **not in v1**
- Replaces language-level `select` for v1
## Mutex and WaitGroup
- **Mutex.Lock** → `Result<MutexGuard, MutexError>`
- **Mutex.TryLock** → `Option<MutexGuard>` (`None` if would block)
- **Mutex.Unlock** — explicit in v1 (no `defer` required for spec)
- **WaitGroup.Wait** parks until counter zero; **Add**/**Done** follow Go semantics
## Console integration
`Console.OnResize` and control `onTick` paths **must** publish into a `Channel<T>` (or **Hub** member) so UI fibers **Receive** events without multicast **event** invocation across fibers. Language **event** syntax may remain for single-fiber use; cross-fiber UI **must** use **Channel** (see console-terminal-events).
Source Record: Concurrency package - Decisions record (legacy index)
Authority: informative provenance
Legacy path: /platform-spec/core-library/concurrency/concurrency-package/articles/decisions-record/
Source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/articles/decisions-record/content.md
SHA-256: 7e3480acafb91a6d4fd8d4fae6c8bc7cba139731e7682639bba91e523f1b3660
Migrated source text
## Purpose
This article is a **legacy URL** retained for bookmarks. Normative decision text **must** be read from **`adr/`** files (`D-CORE-CONC-0001` through `D-CORE-CONC-0014`) and the feature hub **ADRs** reader tab.
## Canonical ADR index
| adrId | Title |
| --- | --- |
| D-CORE-CONC-0001 | [Channel default unbounded](./adr/0001-channel-default-unbounded/) |
| D-CORE-CONC-0002 | [Fiber cancellation](./adr/0002-fiber-cancellation/) |
| D-CORE-CONC-0003 | [Hub round-robin fairness](./adr/0003-hub-round-robin-fairness/) |
| D-CORE-CONC-0004 | [Main fiber shutdown](./adr/0004-main-fiber-shutdown/) |
| D-CORE-CONC-0005 | [Result not panic errors](./adr/0005-result-not-panic-errors/) |
| D-CORE-CONC-0006 | [spawn keyword; reserved async](./adr/0006-spawn-keyword-reserved-async/) |
| D-CORE-CONC-0007 | [GC phase A single mutator](./adr/0007-gc-phase-a-single-mutator/) |
| D-CORE-CONC-0008 | [Scheduler defaults](./adr/0008-scheduler-defaults/) |
| D-CORE-CONC-0009 | [Channel runtime semantics](./adr/0009-channel-runtime-semantics/) |
| D-CORE-CONC-0010 | [Homogeneous Hub v1](./adr/0010-hub-homogeneous-v1/) |
| D-CORE-CONC-0011 | [OnCancelled on handle](./adr/0011-fiber-handle-oncancelled/) |
| D-CORE-CONC-0012 | [Mutex TryLock v1](./adr/0012-mutex-trylock-v1/) |
| D-CORE-CONC-0013 | [Monotonic clock](./adr/0013-monotonic-clock/) |
| D-CORE-CONC-0014 | [Forbidden ancestor Join](./adr/0014-join-ancestor-forbidden/) |
Source Record: Concurrency package - Design model
Authority: informative provenance
Legacy path: /platform-spec/core-library/concurrency/concurrency-package/articles/design-model/
Source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/articles/design-model/content.md
SHA-256: b35ced9a425ebb52f106677c960265fee53b0d3e42761ea1166e79a6fbfebf99
Migrated source text
## Package layout
Workspace package **`corelib_concurrency`** (directory `compiler/corelib/packages/concurrency/`) with one primary module file per public type (match existing corelib style: `System/Syscall/` split).
| Module path | Responsibility |
| --- | --- |
| `Concurrency.Fiber` | `Fiber<T>` struct; **`event OnCancelled()`** on handle; **Join**, **Detach**, **Cancel** |
| `Concurrency.Channel` | `Channel<T>`; **Send**, **Receive**, **TrySend**, **TryReceive**, **Close** |
| `Concurrency.ChannelOptions` | `Bounded(capacity)`, `Unbounded`, optional **SingleReader** / **SingleWriter** flags |
| `Concurrency.Hub` | `Hub<T>` homogeneous; **Register**, **WaitReceive** (round-robin) |
| `Concurrency.HubReceiveResult` | `index` + `value: T` |
| `Concurrency.Mutex` | **Lock**, **TryLock**, **Unlock** |
| `Concurrency.WaitGroup` | **Add**, **Done**, **Wait** |
| `Concurrency` | **Yield**, **NowMillis**, **ProcessorCount**, **SetProcessorCount** |
Prelude re-exports (optional, versioned): only what most apps need—avoid dumping the whole package into `Prelude.bd` until stable.
## Status code layout
Runtime status values are split by namespace. `Concurrency.Status` is only for channel, hub, mutex, and wait-group paths: `Ok = 0`, `Closed = 1`, `Cancelled = 2`, `WouldBlock = 3`, `HubEmpty = 4`, `HubLimit = 5`, `HubNotFound = 6`, and `MutexBusy = 1` for try-lock. `Concurrency.FiberJoinStatus` is a separate join namespace: `Ok = 0`, `Cancelled = 1`, `Panicked = 2`, `StackOverflow = 3`, and `NotDone = 4`.
Fiber **Join** mappings must never compare join results against channel/hub status constants. Hub **Register** and **WaitReceive** map only hub-specific statuses to `HubError`.
## Thin-wrapper rule
Each public method maps **1:1** to a runtime builtin or a small inline sequence documented in **[Channels and synchronization](/platform-spec/execution/runtime/channels-and-synchronization/)**. No intermediate “scheduler interface” trait hierarchy in Beskid source.
## Reference implementation note
Runtime fiber switching may follow a **stack-switching coroutine** design (see [Building lightweight coroutines in Rust (fibers)](https://medium.com/@ksaritek/building-lightweight-coroutines-in-rust-introducing-rust-fibers-53b91625a9de) for engineering patterns). The **normative contract** remains this platform spec and ABI symbols, not the article.
## Anti-patterns (forbidden)
- Parallel .NET-style stacks (`Task`, `ValueTask`, `ChannelReader`/`ChannelWriter` split types in user API)
- Hidden `async`/`await` lowering
- Second channel abstraction in `System.*` outside this package
Source Record: Concurrency package - Examples
Authority: informative provenance
Legacy path: /platform-spec/core-library/concurrency/concurrency-package/articles/examples/
Source: site/spec-content/platform-spec/core-library/concurrency/concurrency-package/articles/examples/content.md
SHA-256: d0f330dd3dcb7bad6f561cf52197d6f88e16241d806ece2536323292cb44bf09
Migrated source text
Informative sketches (syntax may evolve with `spawn` lowering):
```beskid
use Concurrency.Channel;
use Concurrency.Fiber;
use Core.Results.Result;
// spawn keyword lowers to Fiber handle; Join for result
Fiber<i64> worker = spawn Compute();
Result<i64, FiberError> outcome = worker.Join();
Channel<string> log = Channel(ChannelOptions.Bounded(64));
log.Send("ready");
Result<string, ChannelError> line = log.Receive();
```
**Hub** (homogeneous multichannel wait):
```beskid
`Hub<ConsoleMessage>` hub = `Hub<ConsoleMessage>`.Create();
hub.Register(0, resizeChannel);
hub.Register(1, inputChannel);
Result<HubReceiveResult, HubError> ready = hub.WaitReceive();
// ready.value.index + ready.value.value
```
Unlike payload types: use `enum ConsoleMessage { Resize(ConsoleSize), Key(KeyEvent) }` and `Channel<ConsoleMessage>`, or separate hubs per type.
Console resize path should **Send** into a channel consumed by the UI fiber rather than invoking **event** handlers on arbitrary fibers.