fix(gc): final-root-remark makes budgeted atomic-finalize atomic (#6180 Stage 2)#6235
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughBudgeted GC atomic finalization now performs a final root remark after barrier seed draining, propagates its trace seeds, and continues to weak processing or remembered-set rebuilding. A regression test covers pointers inserted into shadow-stack slots after the initial root scan. ChangesBudgeted GC final root remark
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant GcCycleState
participant RootScanCycleState
participant TraceWorklist
GcCycleState->>GcCycleState: Complete BarrierSeedDrain
GcCycleState->>RootScanCycleState: Re-scan roots
RootScanCycleState-->>TraceWorklist: Add remark trace seeds
GcCycleState->>TraceWorklist: Drain seeds
TraceWorklist-->>GcCycleState: Continue to WeakProcessing or RememberedSetRebuild
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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.
Inline comments:
In `@crates/perry-runtime/src/gc/tests/debt_pacer.rs`:
- Around line 567-583: Update the test setup around js_shadow_slot_set so the
hidden object is introduced through a post-scan root overwrite that bypasses the
store barrier, rather than being marked by
runtime_write_barrier_root_nanbox/incremental_mark_barrier_value. Preserve the
later complete_budgeted_gc_cycle assertions and free-list check so they
specifically validate FinalRootRemark keeping the shadow-slot-only pointer
alive.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 453280dc-6b01-4a95-b69e-29380888e5dd
📒 Files selected for processing (2)
crates/perry-runtime/src/gc/cycle.rscrates/perry-runtime/src/gc/tests/debt_pacer.rs
… Stage 2) The last known incremental soundness gap: a budgeted cycle's RootScan runs ONCE, early. A pointer whose only reference migrates into a stack local mid-cycle (heap slot overwritten - the store barrier shades the NEW value, never the old) is invisible to the one-shot scan and was swept live. AtomicFinalize now runs a FinalRootRemark for budgeted cycles: re-scan every root (shadow stack, globals, registered scanners) with marks nearly final, unbudgeted - bounded by root-set size, not heap - then trace the newly discovered objects before WeakProcessing reads the marks. From remark to the Sweep transition the minor path executes ATOMICALLY in one pause (the dispatcher classifies subphases as sliced vs atomic-tail); the full path's sliced RememberedSetRebuild is the one post-remark window and remains store-covered by the active barrier. The remark also runs with a stable frame_top, subsuming the count-based shadow-cursor staleness of the sliced initial scan. Synchronous cycles skip the remark (no mutator windows, no staleness): zero cost on the default STW path. Regression test: object allocated BEFORE the cycle (not allocate-black) referenced by nothing at RootScan, planted in a shadow slot after the cycle passes RootScan - must survive and stay off the sweep free list.
|
Applied: the regression test now plants the hidden pointer via a RAW shadow-slot write (direct |
7b48029 to
cd283d8
Compare
Part of #6180 Stage 2 (follow-on to #6226). Closes the last known incremental soundness gap: the hide-in-a-local window.
Bug
A budgeted cycle's
RootScanruns once, early. If a pointer's only reference migrates into a stack local mid-cycle — its heap slot overwritten (the store barrier shades the NEW value, never the old) — the object is invisible to the one-shot scan and is swept live. This is the standard reason every production incremental collector has a final remark pause.Fix
AtomicFinalizegains aFinalRootRemarksubphase for budgeted cycles only (synchronous cycles have no mutator windows — zero cost on today's default path):WeakProcessingreads the marks.BarrierSeedDrain, full-cycleRememberedSetRebuild— may return to the mutator) vs atomic tail (remark → weak → barrier-off → Sweep transition — one pause, no mutator windows). The minor path is fully atomic from remark to sweep; the full path's sliced RS-rebuild is the one post-remark window and remains store-covered by the active mark barrier. Remark runs before the full path's RS-rebuild so the rebuilt sticky set sees final marks.frame_top, the remark also subsumes the count-based shadow-stack cursor staleness of the sliced initial scan.Verification
atomic_finalize_remark_rescues_pointer_hidden_in_shadow_slot_after_root_scan: object allocated before the cycle (so whole-cycle allocate-black does not protect it), referenced by nothing at RootScan, planted into a shadow slot only after the cycle passes RootScan — must survive and stay off the sweep free list.perry-runtimelib suite: 1227 passed / 0 failed.gc()± force-evacuate ±PERRY_GEN_GC=0): 6/6 exact-checksum clean on release.Summary by CodeRabbit