Parent: #30
Goal
Define and implement the safe recovery policy for GC-backed generator / iterator-helper side-store allocation pressure under live no-GIL peers.
Why this is separate
#30 has proven no-GIL emergency recovery for GC-cell slabs and safepoint-owned ArrayBuffer byte slabs, while trace-sensitive Object / Promise / Environment / async-generator request critical sections intentionally fail closed.
Generator and iterator-helper side stores are a different seam: the parallel tracer defers their mutable storage to the world-stopped finish path because a running generator's exec stack, async-generator request state, resumable handler buffers, and iterator-helper backing state are not sound to trace directly while mutators run. The allocation-failure collector refuses to sweep while those deferred edges remain, so treating these as an ordinary side-store retry class would either fail to reclaim or risk weakening the tracing invariant.
Current status
The negative/fail-closed policy is now executable for the deferred generator/iterator family:
- Suspended generator stack / execution storage fails closed while no-GIL peers are live.
- Resumable generator handler buffers fail closed while no-GIL peers are live.
- Pending async-generator request buffers fail closed while no-GIL peers are live.
- Iterator-helper backing state fails closed while no-GIL peers are live.
- Deferred-cell aborts are reported separately as
gc_par_deferred_aborts/def-abort, while still preserving the existing round-limit abort accounting identity. docs/threads/limits.mdanddocs/threads/testing.mddistinguish trace-sensitive locks from deferred generator/iterator tracing.
The current design evidence points to a conservative rule: deferred generator/iterator state should not be recovered by the live no-GIL allocation-failure collector unless a future allocation site can prove no deferred edges remain, or the collector can finish through a sound world-stopped path. No positive recoverable generator/iterator class has been proven yet.
Closure policy
Decision after the combined main gate for d0092f5e passed (https://github.com/zig-utils/zig-js/actions/runs/29201642565): no current generator/iterator side-store allocation class is sound to recover under live no-GIL peers. The implemented policy is intentionally all-fail-closed while deferred generator/iterator edges remain pending.
This preserves the tracer invariant: a parallel allocation-failure collector may not claim a sweep while suspended generator stacks, resumable handler buffers, async-generator request buffers, or iterator-helper backing state have been deferred to the world-stopped finish path.
Future work should reopen this tracker or file a narrower follow-up only if a specific allocation site can prove either that no deferred edges remain or that recovery can finish through a sound world-stopped path. Any such positive class needs a focused witness plus the existing negative fail-closed coverage.
Acceptance criteria
- No-GIL TSan and semantic gates remain suppression-free.
- Every newly recoverable generator/iterator allocation class has a bounded focused test.
- Deferred generator/iterator tracing still prevents unsafe parallel sweep; no generic side-store retry bypasses that invariant.
- If no positive class is sound, #30 documents the fail-closed policy as intentional rather than incomplete.