Skip to content

fix(gc): the seeded schedule arms the poll word, like zeal (#7781) - #7782

Merged
proggeramlug merged 5 commits into
mainfrom
gc/schedule-arms-the-poll-word
Aug 10, 2026
Merged

fix(gc): the seeded schedule arms the poll word, like zeal (#7781)#7782
proggeramlug merged 5 commits into
mainfrom
gc/schedule-arms-the-poll-word

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Fixed

Summary by CodeRabbit

  • Bug Fixes

    • Fixed scheduled garbage collection so polling remains active when scheduling is enabled.
    • Improved temporary scheduling behavior, including proper activation and release during nested or scoped changes.
    • Ensured scheduled collections run reliably at configured rates across event-loop boundaries.
  • Documentation

    • Added a changelog entry describing the scheduling fix and verification.
  • Chores

    • Updated the application version to 0.5.1451.

Ralph Küpper added 4 commits August 10, 2026 16:52
PERRY_GC_SCHEDULE_RATE=1 saw 6 safepoints against zeal's 9,648 loop polls
on the same reproduction: nothing kept the poll word armed for the schedule
mode, so its loop-safepoint bypass sat behind a gate that never opened.
resolve_poll_seed keeps the seed when the schedule is enabled, and
ScheduleGuard mirrors ZealGuard's arm/release pair.

Claude-Session: https://claude.ai/code/session_01Y1QZ5wUP9gRSwpiweT4Wix
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: eaef67d6-5391-4579-b9c1-4d29a0bf8a69

📥 Commits

Reviewing files that changed from the base of the PR and between 8d098fa and 9adcf51.

📒 Files selected for processing (1)
  • crates/perry-runtime/src/gc/schedule.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/perry-runtime/src/gc/schedule.rs

📝 Walkthrough

Walkthrough

Scheduled GC now keeps the GC poll word armed at startup and during ScheduleGuard activation. Guard cleanup restores the previous poll state. A regression test verifies activation and release. Version metadata and changelog documentation were updated.

Changes

GC schedule poll arming

Layer / File(s) Summary
Scheduled GC poll arming and lifecycle
crates/perry-runtime/src/gc/poll_arm.rs, crates/perry-runtime/src/gc/schedule.rs, crates/perry-runtime/src/gc/tests/triggers.rs, changelog.d/7782-schedule-arms-poll-word.md, Cargo.toml, CLAUDE.md
Scheduled GC preserves the startup poll seed and updates poll arming when ScheduleGuard activates, deactivates, or restores an override. Regression coverage verifies the poll-word changes. The changelog and version metadata reflect the fix.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

  • PerryTS/perry issue 7781 — Directly tracks the scheduled GC poll-word arming fix and regression test.

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the fix and regression results but omits the required template sections, test checklist, and explicit test commands. Reformat the description with Summary, Changes, Related issue, Test plan, and Checklist sections, and list the exact commands and results.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main GC scheduling fix and its relationship to zeal behavior.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch gc/schedule-arms-the-poll-word

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/perry-runtime/src/gc/poll_arm.rs (1)

128-128: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for startup schedule arming.

The new regression test exercises ScheduleGuard::set, which calls arm_poll() directly. It does not exercise resolve_poll_seed() or schedule::gc_schedule_enabled(). Removing this condition would still leave that test passing. Add a test for startup seed resolution with scheduled GC enabled, or factor the predicate into a separately testable helper.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/perry-runtime/src/gc/poll_arm.rs` at line 128, Add targeted coverage
for the startup schedule-arming predicate near ScheduleGuard::set and arm_poll:
enable scheduled GC and verify startup seed resolution through resolve_poll_seed
or schedule::gc_schedule_enabled reaches the expected arming behavior.
Alternatively, extract the combined
gc_zeal_enabled/schedule::gc_schedule_enabled predicate into a helper and test
both enabled and disabled cases, ensuring the regression test would fail if
either condition were removed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/perry-runtime/src/gc/poll_arm.rs`:
- Line 128: Add targeted coverage for the startup schedule-arming predicate near
ScheduleGuard::set and arm_poll: enable scheduled GC and verify startup seed
resolution through resolve_poll_seed or schedule::gc_schedule_enabled reaches
the expected arming behavior. Alternatively, extract the combined
gc_zeal_enabled/schedule::gc_schedule_enabled predicate into a helper and test
both enabled and disabled cases, ensuring the regression test would fail if
either condition were removed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bf4fe85a-8d9a-4ae7-aa3a-5b4cc633ae45

📥 Commits

Reviewing files that changed from the base of the PR and between c2a96b6 and 8d098fa.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • CLAUDE.md
  • Cargo.toml
  • changelog.d/7782-schedule-arms-poll-word.md
  • crates/perry-runtime/src/gc/poll_arm.rs
  • crates/perry-runtime/src/gc/schedule.rs
  • crates/perry-runtime/src/gc/tests/triggers.rs

…poll saturates

off()-disarm-then-Drop-rearm leaks +1 permanently when the disarm lands on
a zero word (saturation loses the decrement, the paired arm does not). The
leak pinned the poll armed for the rest of the test binary: the timing test
slowed and the generation-gate contract took a safepoint drain mid-stage,
2/2 consistently. Only set() arms; only its own Drop releases. 3/3 full-suite
runs clean after.

Claude-Session: https://claude.ai/code/session_01Y1QZ5wUP9gRSwpiweT4Wix
@proggeramlug

Copy link
Copy Markdown
Contributor Author

Merging as v0.5.1451 — and this is the #7741 precondition, demonstrated

Found testing the maintainer's stated condition for #7741: that SCHEDULE_RATE=1 be shown equivalent to zeal on a real reproduction, not asserted. It measurably was not:

before after this fix
rest_argument_rooting safepoints=6 9,653 — matching zeal's 9,648 loop polls
same_module_call_rooting safepoints=6 19,253 — matching zeal's 19,248

Both clean under PERRY_GC_PROTECT_FROMSPACE=1 at depth 800. The "collect at every opportunity" end of the dial saw six opportunities, because #7735's poll arming word was kept armed for zeal and nothing armed it for the schedule — the loop-safepoint bypass #7317 added sat behind a gate that never opened. This is #7735's own design note played out: the word is a counter of reasons, and a new mode whose decision lives inside the safepoint must register itself as one.

The bug I shipped inside the first version of this fix, kept on the record

The first ScheduleGuard mirrored ZealGuard symmetrically — off() disarmed and its Drop re-armed. disarm_poll saturates at zero, so a disarm landing on a zero word is lost while the paired re-arm is not: a permanent +1 leak that pins the poll armed for the rest of the process. In the test binary that surfaced as two consistent failures I first misread as load flakes — the timing test slowed (every loop poll in 2,000 parallel tests paying a call), and the generation-gate contract took a safepoint drain mid-stage. Full-suite on pristine main at the same load: clean. That control is what separated "my leak" from "load".

The bookkeeping is now deliberately asymmetric — only set() arms, only its own Drop releases, off() never touches the word — with the saturation rationale in the comment. Over-arming for a guard's lifetime costs a wasted call; a leaked arm is forever. 3/3 full-suite runs clean after (2,035 passed).

The saturation that caused this is itself correct (#7735 chose it so an underflow could not pin the word armed via wrap) — the lesson is that saturating operations break pair-wise symmetric protocols, and any future arm/disarm pair around this word needs the same asymmetric shape.

the_schedule_holds_the_poll_word_armed_like_zeal mirrors the zeal test and is sabotage-verified. Gates 21/21.

#7741 is now unblocked on its stated condition.

@proggeramlug
proggeramlug merged commit 9499a8c into main Aug 10, 2026
1 of 18 checks passed
@proggeramlug
proggeramlug deleted the gc/schedule-arms-the-poll-word branch August 10, 2026 15:27
proggeramlug pushed a commit to jdalton/perry that referenced this pull request Aug 10, 2026
…down abort

Rebase resolution: main's landed PerryTS#7782 ScheduleGuard (poll-word arming) and
PerryTS#7737/PerryTS#7742 policy content are kept; the branch's schedule pacing, verdict,
instruments split and doc rewrites apply on top. Audit found every seeded run
aborting at exit (worker teardown + std::thread::current after TLS
destruction) — fixed via an OS-id diagnostics-owner gate, once-guarded
verdict, and a regression test.

Claude-Session: https://claude.ai/code/session_01Y1QZ5wUP9gRSwpiweT4Wix
proggeramlug pushed a commit that referenced this pull request Aug 10, 2026
…e covers it (#7741)

* gc: retire PERRY_GC_ZEAL for the seeded schedule

PERRY_GC_SCHEDULE_RATE=1 selects every candidate safepoint, and
PERRY_GC_SCHEDULE_ALLOC_KB carries the allocation pacing (#7728) with the same
4 KB default and the same 0 escape hatch, so the removed knob has no behaviour
the schedule cannot reach. Two knobs differing only in how they pick
safepoints are two configurations to keep exercised, and keeping both had
already forced a precedence rule to stop their counters double-counting one
minor.

The #7604 liveness counters move to gc/instruments.rs -- they count what the
collector did, not what forced it -- and feed both the schedule's exit summary
and schedule_liveness_report, which keeps the verdict's three causes and its
exit 70 at the rate-1 endpoint. Below the endpoint a sampling run that forces
nothing is legitimate (RATE=0 is the control arm), so no verdict is issued
there.

Every removed test has a schedule counterpart; three were dropped as exact
duplicates. The smoke script's two stress arms run against the schedule with
their non-vacuity assertions intact.

Review follow-ups included: a blocked safepoint no longer charges the pacing
stride, PERRY_GC_SCHEDULE_ALLOC_KB clamps rather than saturating into an off
switch, the poll-word arming test drives the real resolution in both
directions, and gc_schedule_fuzz.sh rejects an out-of-range rate instead of
printing a reproduce command for a density it did not run at.

* gc: rebase the zeal retirement onto v0.5.1457 and fix the worker-teardown abort

Rebase resolution: main's landed #7782 ScheduleGuard (poll-word arming) and
#7737/#7742 policy content are kept; the branch's schedule pacing, verdict,
instruments split and doc rewrites apply on top. Audit found every seeded run
aborting at exit (worker teardown + std::thread::current after TLS
destruction) — fixed via an OS-id diagnostics-owner gate, once-guarded
verdict, and a regression test.

Claude-Session: https://claude.ai/code/session_01Y1QZ5wUP9gRSwpiweT4Wix

* chore: bump version to 0.5.1458

Claude-Session: https://claude.ai/code/session_01Y1QZ5wUP9gRSwpiweT4Wix

---------

Co-authored-by: Ralph Küpper <ralph@skelpo.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant