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
{{ message }}
Repository navigation
gc: PERRY_GC_ZEAL forces a collection at EVERY back-edge poll, so zeal no longer terminates on real workloads #7728
PERRY_GC_ZEAL=1 is the primary instrument for catching moving-GC correctness bugs (paired with PERRY_GC_PROTECT_FROMSPACE=1 it produced the precise fault that diagnosed #7682). It no longer completes on a real workload.
Repro
gc-handoff/apps/iso_miss.ts — a tree-walking interpreter, 40 rounds of fib 21.
build
config
wall
ca8c0d617 (0.5.1418)
no zeal
19 s, checksum 437840 misses 0
ca8c0d617
PERRY_GC_ZEAL=1
timeout at 240 s, no output
f3e14a61e (0.5.1389)
PERRY_GC_ZEAL=1
instant, correct
It is not a livelock, and f3e14a61e was not "working"
Measured on the pinned quiet host, scaling the round count:
rounds
loop_polls
forced collections
wall
1
70,963
70,968
36.3 s
2
141,926
141,931
72.7 s
Perfectly linear, so 40 rounds is ~24 minutes rather than a hang. Zeal forces a collection at every back-edge poll: ~511 µs of fixed per-collection cost (root scan over the shadow stack plus ~55 side-table scanners) to relocate a mean of 5.9 objects. Practically all the work is the collection's fixed overhead, not the relocation zeal exists to stress.
The f3e14a61e endpoint was fast because it was vacuous: PERRY_GC_MOVING_LOOP_POLLS was default-OFF there (#7161), so a compute-only program reached no loop safepoint and zeal forced nothing. That build predates the #7604 exit verdict, so it exited 0 in silence. #7721 flipped the poll default ON — correctly, it is a large collector win — and in the same commit turned zeal from free-and-vacuous into correct-but-unusable.
Confirmed by isolation: the oldf3e14a61e compiler with PERRY_GC_MOVING_LOOP_POLLS=1 forced at compile+run time already takes 35.8 s for one round under zeal (0.62 s without). So no commit "broke" zeal; zeal was never paced, and the poll default is what exposed it.
#7254 already logged "a striking concentration of multi-minute-plus runs" under this pairing and left the population untriaged. This is that triage.
Why it matters
An instrument that does not terminate is an instrument nobody switches on, and this bug class (a stale pointer across a moving collection, #7154 family) has shipped more than once. scripts/gc_instrument_smoke.sh cannot see the problem because its fixture is deliberately sized at ~1200 polls "so the zeal arm costs seconds rather than minutes" — at that size, every-poll and paced are indistinguishable.
Fix direction
Pace zeal by allocation, the way V8 (--gc-interval) and SpiderMonkey (gcZeal(mode, frequency)) do, with an escape hatch restoring literal every-poll semantics, plus a budgeted CI arm at a realistic poll count so this cannot regress silently again.
Activity
gate(gc): widen the zeal-termination budget and refresh the CI step c…
docs(gc): note zeal's allocation pacing in the memory-model and rooti…
docs(gc): replace the stride rationale with the measured sweep (#7728)
gate(gc): make arm 6's fixture escape and key the check on the collec…
gate(gc): budgeted zeal-termination arm, docs, and renumber to #7728
gate(gc): widen the zeal-termination budget and refresh the CI step c…
docs(gc): note zeal's allocation pacing in the memory-model and rooti…
docs(gc): replace the stride rationale with the measured sweep (#7728)
gate(gc): make arm 6's fixture escape and key the check on the collec…
fix(gc): pace PERRY_GC_ZEAL by allocation so the instrument terminates (
feat(gc): seeded GC-schedule fuzzing (PERRY_GC_SCHEDULE_SEED)
gc: retire PERRY_GC_ZEAL for the seeded schedule
gc: retire PERRY_GC_ZEAL for the seeded schedule
gc: retire PERRY_GC_ZEAL for the seeded schedule
Remove the older collector stress-test setting, now that the newer on…