fix: harden Phase 2 async/await per adversarial review (io.sx)

- await: add the one-awaiter-per-future guard `sched.Task.wait` has — a second
  concurrent `await` on the same pending future would overwrite the single
  `park` handle and orphan the first awaiter (silent deadlock). Now aborts
  loudly. (Fan-in over SEPARATE futures — `race` — registers one awaiter each,
  so it stays fine.)
- Document the Future/ThunkBox ALLOCATOR-LIFETIME contract: both are allocated
  from the `context.allocator` in force at `async`, which must outlive the
  future (the long-lived-container rule). Calling `async` inside a transient
  arena torn down before `run()` is a use-after-free; the common case (program
  GPA) is safe. A deeper own-allocator capture is deferred to convergence.
- Document that `cancel` does NOT stop an already-spawned worker (model (a) —
  the worker still runs; the sticky `canceled` atomic is the source of truth).
  True work-cancellation is Phase 3.
- Drop the dead `f.task = null` (immediately overwritten by spawn_raw).

The new `io_abort` extern shifts the prelude type table — 40 `.ir` snapshots
regenerated (behavior-preserving; no `.exit`/`.stdout`/`.stderr` changed).
Suite 829/0.
This commit is contained in:
agra
2026-06-27 08:13:57 +03:00
parent 967aed67d4
commit ada8d16256
41 changed files with 159 additions and 10 deletions

View File

@@ -4326,6 +4326,9 @@ declare i64 @now_secs(ptr) #0
; Function Attrs: nounwind
declare i64 @mono_ms(ptr) #0
; Function Attrs: nounwind
declare void @abort() #0
; Function Attrs: nounwind
define internal ptr @CBlockingIo.spawn_raw(ptr %0, ptr %1, ptr %2, ptr %3, { i64 } %4) #0 {
entry: