Skip to content

fix(gc): trace large arrays by allocation bounds - #10223

Closed
proggeramlug wants to merge 2 commits into
mainfrom
fix/10215-large-array-gc
Closed

proggeramlug wants to merge 2 commits into
mainfrom
fix/10215-large-array-gc

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Keep large arrays visible to the collector after backing-store growth. gc_element_slot_range now checks capacity against the actual allocation size, preserving the allocated live prefix for marking and the shared mutable-slot visitor.

Changes

  • Remove the unrelated capacity cutoff while retaining allocation bounds and sparse-array handling.
  • Add regression coverage for live ranges, slot enumeration, and child marking at 9, 10, and 16 million elements, plus invalid capacity and sparse/consumed-prefix bounds.
  • Add a changelog fragment.

Source history places the capacity-based regression in 70eaabe57628acbe04d285013ac06ad66a9f2862 (#9434), which changed the tracing guard from live length to capacity. This attribution comes from source history, not an execution bisect.

Related issue

Fixes #10215.

Test plan

  • New runtime regression tests: 2 passed.
  • Restore the old guard temporarily: the regression fails on the range assertion, before child marking or any sweep/move.
  • Formatting and test-registration checks pass.
  • ./scripts/test_affected_crates.sh --base bb9aa5a641: 4,914 tests passed, zero failed (runtime: 3,771 passed / 4 ignored; compiler: 1,106 passed; FFI: 37 passed; updater/UI had no unit tests). Run single-threaded as required, with local target storage, debug info disabled, and incremental compilation disabled.
  • BASE_SHA=bb9aa5a641 SKIP_COMPILE_GATES=1 ./scripts/run_lint_gates.sh: 76 of 77 executable script checks passed; the unchanged public benchmark artifact fails freshness. Two CI-only checks and the compile tier were not run by this command.
  • Full issue acceptance matrix: large push/churn runs with forced collections and the bug(regex): split and replace throw "Regular expression work limit exceeded" on 32,000-unit strings Node handles in under a millisecond #10164 million-record replacement checksum have not been run.

The public benchmark freshness failure predates this patch: all fingerprint inputs and the artifact are byte-identical to base bb9aa5a641. CI also reports an existing thread-local policy violation in crates/perry-runtime/src/regex/perex_owner.rs; that file and the policy inputs are unchanged by this PR. The CI warnings job also fails on the existing unused registered_extern_handle and wasm_memory_descriptor_maximum helpers in object/global_this_webassembly.rs (also present in the initial base build). Formatting passes on the final source.

Local validation ran on macOS arm64. No release TypeScript churn matrix, execution bisect, or million-record Node checksum comparison is claimed.

Checklist

  • Workspace version, CLAUDE.md and CHANGELOG.md are unchanged.
  • Regression tests are registered in the runtime unit suite.
  • Conventional commit and changelog fragment included.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed garbage collection for very large arrays so live element references remain preserved after arrays grow.
    • Improved handling of sparse arrays and arrays with partially allocated storage, preventing valid references from being missed or invalid memory ranges from being scanned.
  • Tests

    • Added regression coverage for large, sparse, and dynamically grown arrays, including scenarios with millions of elements.

@coderabbitai

coderabbitai Bot commented Sep 13, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: a59e9c09-3557-489d-8c0a-652cba3740ed

📥 Commits

Reviewing files that changed from the base of the PR and between bb9aa5a and fb5b205.

📒 Files selected for processing (4)
  • changelog.d/10223-large-array-tracing.md
  • crates/perry-runtime/src/array/header_gc_slots.rs
  • crates/perry-runtime/src/gc/tests/layout_trace.rs
  • crates/perry-runtime/src/gc/tests/layout_trace/large_array_slots.rs

Included review availability: Your plan provides up to 8 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

The collector now bounds array slot tracing by tracked physical allocation instead of a fixed 16-million-element cutoff. Regression tests cover large live prefixes, child marking, invalid capacities, sparse arrays, and consumed queue prefixes.

Changes

Large array GC tracing

Layer / File(s) Summary
Physical allocation slot bounds
crates/perry-runtime/src/array/header_gc_slots.rs
gc_element_slot_range no longer rejects capacities above 16 million when they fit the tracked physical allocation.
Array tracing regression coverage
crates/perry-runtime/src/gc/tests/layout_trace.rs, crates/perry-runtime/src/gc/tests/layout_trace/large_array_slots.rs, changelog.d/10223-large-array-tracing.md
New tests validate live-prefix enumeration, child marking, invalid capacities, sparse bounds, and consumed queue prefixes. The changelog records the fix and coverage.

Priority: ➖ Normal

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

Change: Bug fix · Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to fb5b2

The large-array tracing fix has targeted regression coverage, and no concrete merge-blocking risk remains.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The implementation addresses the tracing cause in [#10215]. gc_element_slot_range now validates capacity against array_physical_capacity, and large_array_slots.rs tests 9M, 10M, and 16M live p… Complete and record automated acceptance coverage for [#10215] at 9M, 10M, and 16M elements with and without churn and forced collections. Complete and record the #10164 1M-record replace checksum comparison against Node. Keep the regress…
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 3 files. (1 skipped: 1… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed The changed runtime guard, GC layout-trace tests, module registration, and changelog entry directly support [#10215]. The tests validate allocation bounds and preservation of live array references. No…
Title check ✅ Passed The title clearly and concisely describes the main change: fixing garbage-collector tracing for large arrays by using allocation bounds.
Description check ✅ Passed The description includes the required Summary, Changes, Related issue, Test plan, and Checklist sections. It clearly documents the fix, regression coverage, validation results, known pre-existing fail…
Full details: Linked Issues check

Explanation

The implementation addresses the tracing cause in [#10215]. gc_element_slot_range now validates capacity against array_physical_capacity, and large_array_slots.rs tests 9M, 10M, and 16M live prefixes, marking, invalid capacity, sparse length, and consumed-prefix bounds. The PR also identifies commit 70eaabe57628acbe04d285013ac06ad66a9f2862 as the regression source. However, the linked issue acceptance is not complete. The 9M, 10M, and 16M end-to-end reproductions with and without churn and forced collections remain pending. The #10164 1M-record replace checksum comparison also remains pending.

Resolution

Complete and record automated acceptance coverage for [#10215] at 9M, 10M, and 16M elements with and without churn and forced collections. Complete and record the #10164 1M-record replace checksum comparison against Node. Keep the regression test that fails when this fix is reverted.

Full details: Docstring Coverage

Explanation

Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 3 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/10215-large-array-gc

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.

@proggeramlug
proggeramlug marked this pull request as ready for review September 14, 2026 02:25
@proggeramlug

Copy link
Copy Markdown
Contributor Author

Coordination for the brief 3 runtime-handle performance session: working in perf/runtime-handle-scopes from 9fda98d, isolated worktree. Expected edits are gc/roots/runtime_handles.rs, tls_hot.rs, and focused runtime-root tests / custody inventories. I checked this PR’s file list: no overlap with header_gc_slots.rs or layout_trace tests, and I will keep the array tracing change out of this branch. Please flag any planned runtime-handle or hot-TLS edits.

@proggeramlug

Copy link
Copy Markdown
Contributor Author

Flagging an overlap for the runtime-handle / hot-TLS session: #10244 (Android pthread-key pooling) edits crates/perry-runtime/src/tls_hot.rs — it swaps std::thread::AccessError for a tls_hot::AccessError alias, cfg-gates HOT and __perry_thread_local_storage! onto a new tls_os_pool backend on Android, and adds an Android is_destroyed() check in HotKey::try_with. It is queued in merge train 187r, which should land within a few hours, so rebasing onto it afterwards will avoid a conflict.

This PR (#10223) itself is in merge train 186r, currently validating.

@proggeramlug

Copy link
Copy Markdown
Contributor Author

Landed via merge train 186r (#10247) at eb13fa1 on main.

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.

bug(gc): arrays of ~9M+ elements holding heap strings read back corrupted values (silent use-after-free)

1 participant