Skip to content

fix(gc): avoid closure side-table GC deadlock#4956

Merged
proggeramlug merged 3 commits into
PerryTS:mainfrom
andrewtdiz:codex/fix-gc-write-barrier-stress-timeout
Jun 11, 2026
Merged

fix(gc): avoid closure side-table GC deadlock#4956
proggeramlug merged 3 commits into
PerryTS:mainfrom
andrewtdiz:codex/fix-gc-write-barrier-stress-timeout

Conversation

@andrewtdiz

Copy link
Copy Markdown
Contributor

Summary

Fixes the gc_write_barrier_stress::tenured_mutation_stress hang that blocked PR #4928's GitHub Actions cargo-test run after cancellation, separately from the optimized-lib freshness change.

Root cause: during minor copying GC, scan_closure_dynamic_props_roots_mut held the closure dynamic-property/prototype side-table mutexes while visiting JS value slots. Visiting those slots can evacuate a closure, and the move hook closure_dynamic_props_owner_moved re-enters the same side-table lock, deadlocking the runtime inside the first explicit gc() after tenured objects point at nursery values.

The fix detaches each closure side-table entry before invoking GC visitor callbacks, then merges it back under the forwarded owner after callbacks complete. It also prevents non-closure receivers from entering closure-only Function.prototype fallback, which the stress exposed after the deadlock was removed.

Verification

  • Reproduced the original hang locally with a process-group timeout wrapper: cargo test -p perry --test gc_write_barrier_stress tenured_mutation_stress -- --nocapture timed out after 120s and left no main_bin/cargo orphan after wrapper cleanup.
  • Sampled a no-auto optimized reducer at the post-mutation gc() stall. Stack showed scan_closure_dynamic_props_roots_mut -> visit_nanbox_f64_slot -> move_young -> closure_dynamic_props_owner_moved -> CLOSURE_PROPS.lock.
  • cargo test -p perry-runtime closure::dynamic_props::tests_1802 -- --nocapture => 4 passed.
  • cargo build -p perry-runtime => passed, existing warnings only.
  • No-auto fixture after fix: RUN_STATUS 0 timed_out False elapsed 26.0, stdout BARRIER_STRESS_OK.
  • Normal-mode focused test: cargo test -p perry --test gc_write_barrier_stress tenured_mutation_stress -- --nocapture => 1 passed, finished in 150.24s.
  • Full stress file: cargo test -p perry --test gc_write_barrier_stress -- --nocapture => 2 passed, finished in 10.77s.
  • cargo fmt --check --package perry-runtime --package perry => passed.
  • git diff --check => passed.
  • Process audit found no matching leftover gc_write_barrier_stress, main_bin, perry compile, or perry-auto process from this worktree.

CI note

PR #4928's cancelled cargo-test appears blocked by this separate GC/runtime stress issue, not by the optimized-lib freshness assertions. After this lands, rerun PR #4928's cancelled check.

@andrewtdiz andrewtdiz marked this pull request as ready for review June 10, 2026 21:44
@proggeramlug proggeramlug merged commit 0ec5542 into PerryTS:main Jun 11, 2026
13 checks passed
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.

2 participants