You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Follow-up from #29 after zig-gc b99de49, zig-js 78b553a, and zig-js 45a58ce promoted semantics/oom-one-thread.js and added ArrayBuffer byte-slab recovery.
Current status
The landed recovery paths cover these GC-backed capped-context allocation failures:
GC cell slab allocation failures retry after Context.collectForAllocationFailure() when the current thread can safely collect.
no-GIL GC-cell slab recovery has a safepoint-owned current-interpreter path from Runtime: make no-GIL heap-cap recovery safepoint-owned #31; it reuses the abort-safe parallel root-publication collector and retries only after a collection actually sweeps.
GC-backed ArrayBuffer byte allocation failures retry once after the same recovery hook, including a live no-GIL peer witness for safepoint-owned byte-slab pressure.
Budgeted runtime allocations now have a guarded one-shot recovery hook (2d4d3ae0): when the heap cap would be exceeded, the quota allocator can collect safely, reserve again, and continue without scattering ad-hoc retries through object/property code.
GC-backed object side-store garbage can now be reclaimed by allocation-failure recovery under the heap cap in safe single-threaded/GIL-capable contexts (Context heap_limit_bytes object side-store pressure recovers garbage).
72beb646 blocks the generic budget-recovery callback inside GcCellBacking allocator-internal critical sections, so GC-cell chunk OOM fails outward to zig-gc's safe outer Heap.create recovery path instead of self-deadlocking while backing locks are held.
b3c86930 roots pre-reserved heap-limit catch environments in the interpreter's precise environment root stack, so emergency recovery cannot collect a catch scope needed to convert reserved OutOfMemoryError values into JS catch (e) bindings.
Single-threaded and GIL-mode contexts can run emergency collection safely when peers are parked/published under the supported scan policy.
Arena-backed heap caps remain fail-closed and non-reclaimable by design.
That is enough for the PR-249 OOM recovery witness, GC-backed ArrayBuffer byte pressure, ordinary budgeted GC-backed side-store pressure, and the current no-GIL GC-cell / ArrayBuffer emergency-recovery slices, while keeping collection fail-closed when peers could hold unscanned roots, when the current allocation site holds a trace-sensitive lock, or when deferred generator/iterator cells prevent a safe parallel finish.
Side-store policy covered so far
Object side-store pressure is now covered by focused tests:
dropped object side stores are reclaimable by explicit collection, and a second allocation wave can then succeed under the same GC-backed cap;
allocation-failure recovery now also reclaims dropped side stores automatically in safe single-threaded/GIL-capable contexts;
live side-store pressure still fails closed as host error.OutOfMemory, proving the cap is enforced when collection cannot free enough budget;
live no-GIL trace-sensitive lock sites are pinned by regression coverage so the runtime does not collect/retry while object/environment/promise/async-generator request or allocator-internal locks could deadlock with tracing.
The recovery hook is intentionally guarded, recursion-safe, and disabled inside allocator-internal backing locks rather than a blanket retry. Many no-GIL side-store allocations can happen while object property/element, promise, environment, async-generator request, iterator/generator, or other trace-sensitive state is held, while GC tracing may need those same structures.
Deferred generator / iterator policy
#36 now tracks the deferred generator/iterator seam separately because it is not just an ordinary trace-sensitive-lock problem. The current fail-closed witnesses cover:
suspended generator execution storage;
resumable generator handler buffers;
pending async-generator request buffers;
iterator-helper backing state;
telemetry that splits deferred-cell aborts into gc_par_deferred_aborts / def-abort without weakening the round-limit abort accounting identity.
No positive recoverable generator/iterator class has been proven. #36 is closed with the conservative policy that all current deferred generator/iterator side-store pressure fails closed while those edges remain pending, unless a future site can prove the collector can safely sweep through a sound world-stopped path.
Remaining perfection work
Extend abort-safe no-GIL / parallel_js emergency recovery only for allocation classes that can prove the current interpreter can collect without scanning unpublishing peers, holding trace-sensitive locks, or leaving deferred generator/iterator edges unresolved.
Define finer lock-aware no-GIL collect/retry policies for side stores and other non-cell allocations where the allocation site can prove it does not hold locks or deferred state the tracer needs.
Add focused tests for every newly recoverable allocation class; keep fail-closed witnesses for allocation classes that remain unsafe to recover generically.
Keep arena-backed heap caps fail-closed unless a caller explicitly opts into GC-backed recovery.
This should stay separate from #29/#24: the promoted PR-249 witness is covered, but capped allocation recovery is not yet perfect for every allocation class or no-GIL interleaving.
Follow-up from #29 after zig-gc b99de49, zig-js 78b553a, and zig-js 45a58ce promoted
semantics/oom-one-thread.jsand added ArrayBuffer byte-slab recovery.Current status
The landed recovery paths cover these GC-backed capped-context allocation failures:
Context.collectForAllocationFailure()when the current thread can safely collect.2d4d3ae0): when the heap cap would be exceeded, the quota allocator can collect safely, reserve again, and continue without scattering ad-hoc retries through object/property code.Context heap_limit_bytes object side-store pressure recovers garbage).72beb646blocks the generic budget-recovery callback insideGcCellBackingallocator-internal critical sections, so GC-cell chunk OOM fails outward to zig-gc's safe outerHeap.createrecovery path instead of self-deadlocking while backing locks are held.b3c86930roots pre-reserved heap-limit catch environments in the interpreter's precise environment root stack, so emergency recovery cannot collect a catch scope needed to convert reservedOutOfMemoryErrorvalues into JScatch (e)bindings.That is enough for the PR-249 OOM recovery witness, GC-backed ArrayBuffer byte pressure, ordinary budgeted GC-backed side-store pressure, and the current no-GIL GC-cell / ArrayBuffer emergency-recovery slices, while keeping collection fail-closed when peers could hold unscanned roots, when the current allocation site holds a trace-sensitive lock, or when deferred generator/iterator cells prevent a safe parallel finish.
Side-store policy covered so far
Object side-store pressure is now covered by focused tests:
error.OutOfMemory, proving the cap is enforced when collection cannot free enough budget;The recovery hook is intentionally guarded, recursion-safe, and disabled inside allocator-internal backing locks rather than a blanket retry. Many no-GIL side-store allocations can happen while object property/element, promise, environment, async-generator request, iterator/generator, or other trace-sensitive state is held, while GC tracing may need those same structures.
Deferred generator / iterator policy
#36 now tracks the deferred generator/iterator seam separately because it is not just an ordinary trace-sensitive-lock problem. The current fail-closed witnesses cover:
gc_par_deferred_aborts/def-abortwithout weakening the round-limit abort accounting identity.No positive recoverable generator/iterator class has been proven. #36 is closed with the conservative policy that all current deferred generator/iterator side-store pressure fails closed while those edges remain pending, unless a future site can prove the collector can safely sweep through a sound world-stopped path.
Remaining perfection work
parallel_jsemergency recovery only for allocation classes that can prove the current interpreter can collect without scanning unpublishing peers, holding trace-sensitive locks, or leaving deferred generator/iterator edges unresolved.This should stay separate from #29/#24: the promoted PR-249 witness is covered, but capped allocation recovery is not yet perfect for every allocation class or no-GIL interleaving.