`s.race((a: ta, b: tb, …))` takes a named tuple of already-spawned `*Task(..)` handles, suspends the calling fiber until the FIRST task is ready, and returns a comptime-synthesized tagged-union (`RaceResult`) mirroring the tuple's labels — variant NAME = the tuple label, payload = that task's result type. After picking the winner it CANCELS and JOINS every loser, so no loser fiber outlives the call (structured concurrency). - `RaceResult($T) -> Type` projects each `*Task(R)` element to `R` via `field_type(pointee(field_type(T, i)), 0)` and mints the union with `make_enum` (the 0649 composition shape). - `race` Phase 1 registers the caller as waiter on all pending tasks and parks; on wake it DEREGISTERS from every task (a later loser completion must never wake it again) and re-scans, lowest-index-first. Phase 2 builds the winner variant with `make_variant`. Phase 3 cancels + joins each loser one at a time — only the joined loser carries a waiter, so no other completion can wake the caller mid-join. - Join correctness rides a new `Task.finished` flag, set at the very end of the `go` worker body (after the work ran OR was skipped on an early cancel) and checked before parking, so a worker that finishes between the cancel and the park can't be lost. Cancellation is cooperative (M:1, no preemption): a loser parked mid-`sleep` runs to its natural end, its value discarded — `race` returns only once every loser has `finished`. The tuple must be NAMED; a positional `._0`/`._1` form is future work. Locked by examples/concurrency/1821 — three tasks (i64/bool/f64) sleeping 10/20/30ms, shortest wins, losers cancelled + joined; byte-identical on aarch64-macOS and aarch64-linux (deterministic virtual time).
53 KiB
53 KiB