Found while testing #7741's precondition, and it blocks #7741
The maintainer's condition for #7741 (removing PERRY_GC_ZEAL) is that the SCHEDULE_RATE=1 equivalence be demonstrated on one of the four reproductions zeal closed this week. Tested on #7606's rooting tests, same binary (main @ v0.5.1450), same quarantine settings:
| instrument |
rest_argument |
same_module_call |
PERRY_GC_ZEAL=1 |
379 collections, 115,536 moved, 9,648 loop polls |
749 collections, 231,012 moved, 19,248 polls |
PERRY_GC_SCHEDULE_SEED=… RATE=1 |
safepoints=6, 6 collections |
safepoints=6, 6 collections |
RATE=1 is the "collect at every opportunity" end of the dial. It saw six opportunities.
Cause
#7735 collapsed the loop back-edge poll's no-work path to one load of PERRY_GC_POLL_ARMED. The word is kept armed for zeal (resolve_poll_seed retains the startup seed when gc_zeal_enabled()), and ZealGuard arms/releases it for tests. Nothing arms it for the schedule mode — so with schedule-on/zeal-off, every back-edge poll takes the disarmed fast path, js_gc_loop_safepoint is never entered, and the loop-safepoint bypass #7317 added sits behind a gate that never opens. The schedule's only surviving entry point is the microtask-pump boundary: six per run on this test.
This is #7735's own design note played out: the arming word is a counter of reasons, and a new reason (a mode whose collection decision lives inside the safepoint) must register itself. The schedule mode was written before the word existed and was merged across it (#7317's rebase) without gaining an arm.
Fix
resolve_poll_seed keeps the startup seed when schedule::gc_schedule_enabled(), exactly as for zeal.
ScheduleGuard::set/off/Drop mirror ZealGuard's arm/release pair, so test-time schedules reach polls too.
- Regression test
the_schedule_holds_the_poll_word_armed_like_zeal, mirroring the zeal test, sabotage-verified.
- Acceptance: the equivalence table above re-run with the fix must show loop-poll-scale safepoints for
RATE=1.
Found while testing #7741's precondition, and it blocks #7741
The maintainer's condition for #7741 (removing
PERRY_GC_ZEAL) is that theSCHEDULE_RATE=1equivalence be demonstrated on one of the four reproductions zeal closed this week. Tested on #7606's rooting tests, same binary (main @ v0.5.1450), same quarantine settings:PERRY_GC_ZEAL=1PERRY_GC_SCHEDULE_SEED=… RATE=1RATE=1is the "collect at every opportunity" end of the dial. It saw six opportunities.Cause
#7735 collapsed the loop back-edge poll's no-work path to one load of
PERRY_GC_POLL_ARMED. The word is kept armed for zeal (resolve_poll_seedretains the startup seed whengc_zeal_enabled()), andZealGuardarms/releases it for tests. Nothing arms it for the schedule mode — so with schedule-on/zeal-off, every back-edge poll takes the disarmed fast path,js_gc_loop_safepointis never entered, and the loop-safepoint bypass #7317 added sits behind a gate that never opens. The schedule's only surviving entry point is the microtask-pump boundary: six per run on this test.This is #7735's own design note played out: the arming word is a counter of reasons, and a new reason (a mode whose collection decision lives inside the safepoint) must register itself. The schedule mode was written before the word existed and was merged across it (#7317's rebase) without gaining an arm.
Fix
resolve_poll_seedkeeps the startup seed whenschedule::gc_schedule_enabled(), exactly as for zeal.ScheduleGuard::set/off/DropmirrorZealGuard's arm/release pair, so test-time schedules reach polls too.the_schedule_holds_the_poll_word_armed_like_zeal, mirroring the zeal test, sabotage-verified.RATE=1.