ReviewOS

zig-utils/zig-js

GC: relocate active Promise-job native roots across JIT safepoints

#511
Closed chris opened this 9 days ago ·
9 days ago

Problem

The clean representative timing collector for #460 reaches moving young collections while Promise reaction jobs are executing. Interpreter.current_microtask is traced and relocated, but promise.runJob copies the Microtask and Reaction by value and continues using raw *Promise / Value locals after invoking JavaScript. A JIT safepoint can compact the registered root while those native locals retain the old address.

A bounded reproducer on current main (9d0227a3) is:

env LC_ALL=C bench-comparison-zig-js independent_steady representative_async_microtasks 6000 1 8

It fails under strict GC with GC mark of non-GC cell from traceReaction / markValue. The exact parent also fails; #510 fixed a different stale edge in suspended captured VM frames and is not the regression source.

The interrupted full row had reached a 20.5 GB physical footprint, and a 3-second stack sample showed all eight workers active, predominantly in repeated young collection/marking rather than deadlocked.

Required fix

  • Execute jobs through the registered current_microtask storage, not an untracked by-value copy.
  • Add a precise relocatable root representation for raw Promise pointers held by native helpers across JavaScript re-entry / allocation safepoints.
  • Cover trace and relocation structurally and with a real moving-GC Promise handler witness.
  • Re-run the bounded 8-lane async reproducer, focused TSan, the full unit gate, and then resume #460 publication evidence.
  • Preserve PromiseJobs FIFO/checksum semantics; do not weaken strict GC validation or reduce the benchmark workload.

Relationships

  • Blocks #460.
  • Follow-up root class discovered after #510.
  • Related GC recovery umbrella: #476.
9 days ago

Implemented and pushed to main as 33150087.

The fix executes the interpreter-owned current Microtask in place, adds a precise relocatable stack for raw Promise cells, and roots/reloads native Promise helper Values across observable getters and handler calls. This closes the stale native-local addresses exposed by moving JIT safepoints without disabling compaction, JIT, or thenable assimilation.

Validation: the exact eight-lane bounded reproducer passes 7/7 ReleaseFast samples at checksum 91956096; the new requested-compaction regression passes in Debug and under TSan; 20/20 Promise-focused tests and the structural relocation test pass; the full unit suite passes 1,534 with one expected skip and zero failures/leaks.

The correctness crash is closed. The distinct full-scale memory/time growth observed after correctness was restored is now tracked in #512 and continues to block #460.

Sign in to comment on this issue.