diff --git a/current/PLAN-RACE.md b/current/PLAN-RACE.md index 5471ce28..86133789 100644 --- a/current/PLAN-RACE.md +++ b/current/PLAN-RACE.md @@ -81,13 +81,32 @@ Prereqs DONE (each committed + adversarially reviewed + suite-green): end-to-end** (proven by `examples/comptime/0649-comptime-typecall-composition.sx`: reflect a named tuple of `*Box(..)` handles → mint a tagged-union with the tuple's labels, projecting `*Box(A)`→`A`). -**All comptime prerequisites are now in place — the rest is pure-sx library work in sched.sx.** +- **`return` inside `inline if` fixed** (`84c2ae4f`) — the natural early-return-per-arm pattern (a + `return` in an `inline if`/comptime-`case` branch inside an `inline for`) no longer drops the + function's trailing statements. Lets `race` build the winner variant with a clean + `inline if i == { case 0: … else: … }` per-arm form. +- **`make_variant($E, idx, payload)`** added to `modules/std/meta.sx` (`1c26944e`) — the WRITE side of + the metatype triad: construct a minted tagged-union value by variant INDEX (the winner is chosen at + runtime; its label can't be a literal). Pure sx (writes the i64 tag@0 + payload@8). Verified for + complex payloads (struct / string / 40-byte struct). **This resolves the variant-construction gap.** -**NEXT (step 2–4): the actual `race` in `library/modules/std/sched.sx`.** +**ONE remaining blocker for the runtime: GAP 1 — comptime-cursor indexing of a named-tuple VALUE.** +`race(tasks: $T)` must read the i-th task `*Task(T_i)` with its concrete type where `i` is the +`inline for` cursor. Today `tasks[i]` → *"cannot index a value of type '(…)'"* and +`field_value(tasks, i)` returns a **void** Any (tuple field-VALUE reflection is unimplemented — the +0195 family covered count/name/type but not value). Fix options: (a) make `tasks[i]` work with a +comptime cursor on a named-tuple value (mirror packs' `xs[i]`), or (b) implement `field_value` for +tuples + recover the concrete type via `field_type(T, i)`. (a) is cleaner for `race` (direct typed +access). This is the last thing between here and the pure-sx runtime. + +**THEN (pure-sx, unblocked once GAP 1 lands): the runtime in `sched.sx`.** - `RaceResult :: ($T) -> Type` over `*Task(..)` (the 0649 shape, with `Task` instead of `Box`). - `race :: ufcs (self: *Scheduler, tasks: $T) -> RaceResult(T)`: suspend the caller until the FIRST - task is `.ready`; build the winner variant (construct the minted union by the winning index/label — - watch for a possible variant-construction-by-dynamic-index gap); then `cancel` + `wait`-join every - loser before returning (structured). Reuses `suspend_self`/`wake` + `Task.cancel`/`wait`. -- Lock with a 2/3-task example (deterministic winner via `sleep` ordering, asserting losers cancelled). + task is `.ready` (register caller as waiter on all pending; on wake DEREGISTER from all to avoid a + double-wake of the merge — the queue-corruption hazard `wake` guards); winner = first ready; + build it with `make_variant` in the matching `inline for` arm; then `cancel` + JOIN every loser + (needs a `Task.finished` flag set at the end of the `go` body so the joiner distinguishes a + finished worker from a merely-flagged-cancelled one, checked before parking like `wait` checks + `.ready`). Reuses `suspend_self`/`wake`. +- Lock with a 2/3-task example (deterministic winner via `sleep` ordering; assert losers cancelled). - Validate byte-identical on aarch64-macOS host AND aarch64-linux container; full `zig build test`.