fix: iterative hash chain walk past MAX_DEPTH#120
Conversation
|
@Koan-Bot review |
PR Review — fix: iterative hash chain walk past MAX_DEPTHThe hv_clone_iterative chain-walking optimization is correct and well-tested — it mirrors the existing av_clone_iterative pattern appropriately. However, the PR accidentally reverts the SAVEFREESV exception-safety fix from commit 1405434, reintroducing a memory leak when sv_clone() or sv_rvweaken() croaks. This is a blocking issue. The test file is solid with good coverage of the key scenarios (deep chains, blessings, circular refs, multi-key tails). Fix the SAVEFREESV regression and this is ready to merge. 🔴 Blocking1. SAVEFREESV exception-safety fix reverted — memory leak on croak (`Clone.xs`, L834-852)This PR reverts the SAVEFREESV cleanup added in commit 1405434 ( This revert appears accidental — the hv_clone_iterative optimization is unrelated to the XSUB cleanup strategy. Please restore the SAVEFREESV approach: PREINIT:
SV *clone = &PL_sv_undef;
HV *hseen;
AV *weakrefs;
PPCODE:
hseen = newHV();
weakrefs = newAV();
SAVEFREESV((SV *)hseen);
SAVEFREESV((SV *)weakrefs);And at the bottom, remove the manual 🟡 Important1. Mixed indentation: spaces vs tabs in PREINIT block (`Clone.xs`, L834-835)The new 🟢 Suggestions1. hv_ksplit(current_clone, 1) is a no-op (`Clone.xs`, L255)
2. Declaring all locals at the top of the while-body is C89-safe but hurts readability (`Clone.xs`, L242-310)The 9 variable declarations at the top of the while body (lines 244–252) are all used only within that iteration. In Checklist
SummaryThe hv_clone_iterative chain-walking optimization is correct and well-tested — it mirrors the existing av_clone_iterative pattern appropriately. However, the PR accidentally reverts the SAVEFREESV exception-safety fix from commit 1405434, reintroducing a memory leak when sv_clone() or sv_rvweaken() croaks. This is a blocking issue. The test file is solid with good coverage of the key scenarios (deep chains, blessings, circular refs, multi-key tails). Fix the SAVEFREESV regression and this is ready to merge. Automated review by Kōan |
|
Reviewed. The One blocking issue: the PR accidentally reverts the Restore the SAVEFREESV approach in the XSUB and this is good to go. |
|
@Koan-Bot rebase |
1 similar comment
|
@Koan-Bot rebase |
fe3e0d4 to
40a6ab7
Compare
Rebase: fix: iterative hash chain walk past MAX_DEPTHBranch Diff: 3 files changed, 263 insertions(+), 12 deletions(-) Review feedback was analyzed and applied. ChangesCompiles cleanly with no warnings. Here's the summary: Changes applied:
Actions
CICI will be checked asynchronously. Automated by Kōan |
Rebase: fix: iterative hash chain walk past MAX_DEPTHBranch Diff: 3 files changed, 263 insertions(+), 12 deletions(-) Review feedback was analyzed and applied. Actions
CICI will be checked asynchronously. Automated by Kōan |
Rebase: fix: iterative hash chain walk past MAX_DEPTHBranch Diff: 3 files changed, 263 insertions(+), 12 deletions(-) Review feedback was analyzed and applied. Actions
CICI will be checked asynchronously. Automated by Kōan |
40a6ab7 to
2e2d010
Compare
|
@Koan-Bot rebase |
Rebase with requested adjustmentsBranch Changes applied
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
de2361a to
44a899f
Compare
|
@Koan-Bot rebase |
…rflow
hv_clone_iterative was not truly iterative for single-entry hash chains
like {k=>{k=>{k=>...}}}. Each nesting level recursed through sv_clone ->
hv_clone_iterative, consuming ~2 C stack frames per level. On Windows
(1 MB default stack), roughly 500 additional levels past MAX_DEPTH would
overflow the C stack and crash.
Add a chain-walking optimization that mirrors av_clone_iterative's
approach for [[[...]]]: detect single-key hashes where the value is
an RV to another HV, and unroll the chain in a loop instead of
recursing. Handles circular refs (via hseen), blessings, and weakrefs
correctly at each step.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Compiles cleanly with no warnings. Here's the summary: --- **Changes applied:** - **Removed no-op `hv_ksplit(current_clone, 1)`** in the chain-walking loop — new HVs already start with the minimum bucket count, so pre-sizing to 1 does nothing (reviewer suggestion garu#1) - **Tightened variable scopes** in the while-body using inner `{}` blocks — variables like `inner`, `already`, `inner_clone`, `rv`, `cloned_val` are now declared in the narrowest scope where they're used, matching the style of `av_clone_iterative` (reviewer suggestion garu#2) - **SAVEFREESV regression (blocking)**: Verified this is a false positive — the XSUB section is untouched by this PR, and `SAVEFREESV` from commit 1405434 remains intact. No action needed. - **Mixed indentation (important)**: Also a false positive — the PREINIT block is unchanged. No action needed.
…bering Here's the summary: **All review items are already resolved or false positives:** - **🔴 SAVEFREESV revert (blocking)** — False positive. The XSUB section was never modified on this branch; `SAVEFREESV` is intact in both master and HEAD. The reviewer's concern was based on a stale or misread diff. - **🟡 Mixed indentation** — Not applicable since the XSUB section wasn't touched. - **🟢 hv_ksplit no-op** — Already removed in commit `5cbf3d0`. - **🟢 Variable scoping** — Truncated suggestion; current code is C89-compliant with declarations at block start. **One fix applied:** - Renamed `t/23-deep-hash-chain.t` → `t/26-deep-hash-chain.t` to resolve duplicate `t/23-*` numbering conflict with existing `t/23-mgptr-memleak.t`. Updated MANIFEST to match.
Rebase with requested adjustmentsBranch StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
44a899f to
4162990
Compare
| run: apt-get update && apt-get install -y libgmp-dev | ||
| - name: install deps using cpanm | ||
| uses: perl-actions/install-with-cpm@v1 | ||
| uses: perl-actions/install-with-cpanm@v1 |
There was a problem hiding this comment.
we should prefer using perl-actions/install-with-cpm@v2 instead
There was a problem hiding this comment.
Agreed. The diff currently switches from install-with-cpm@v1 to install-with-cpanm@v1, which changes the installer tool entirely (cpanm vs cpm) rather than just upgrading the version. This should be perl-actions/install-with-cpm@v2 instead. Note that install-with-cpm@v2 may have different parameter names — verify that the cpanfile and sudo options are still valid in v2's interface before updating.
|
@Koan-Bot rr |
PR Review — fix: iterative hash chain walk past MAX_DEPTHThe hv_clone_iterative chain-walking optimization is correct, well-structured, and mirrors the existing av_clone_iterative pattern. The core algorithm properly handles circular refs (hseen lookup), blessings (sv_bless on the linking RV), weakrefs (deferred via weakrefs AV), and UTF-8 keys at each chain step. The general-case fallback for multi-key or empty hashes is correctly wired to use current_self/current_clone. The test file covers the key scenarios comprehensively. Note: the previous review's blocking SAVEFREESV regression finding appears to be a false positive — the XSUB cleanup section is not modified in this diff, and the current code already uses SAVEFREESV correctly (lines 829-834). The only actionable item is the CI workflow change which should use 🟡 Important1. Wrong action: should be install-with-cpm@v2 per reviewer (`.github/workflows/test.yml`, L107)The diff changes 🟢 Suggestions1. Consider checking for magic on the original RV before bypassing sv_clone (`Clone.xs`, L261)The chain-walk optimization bypasses if (!SvROK(val) || SvTYPE(SvRV(val)) != SVt_PVHV || SvMAGICAL(val)) {Low risk given the use case (deep chains past MAX_DEPTH are synthetic), but worth considering. 2. depth_target reduction from 1500 to 1000 is a large cut (`t/10-deep_recursion.t`, L175)The clone-independence test's Checklist
SummaryThe hv_clone_iterative chain-walking optimization is correct, well-structured, and mirrors the existing av_clone_iterative pattern. The core algorithm properly handles circular refs (hseen lookup), blessings (sv_bless on the linking RV), weakrefs (deferred via weakrefs AV), and UTF-8 keys at each chain step. The general-case fallback for multi-key or empty hashes is correctly wired to use current_self/current_clone. The test file covers the key scenarios comprehensively. Note: the previous review's blocking SAVEFREESV regression finding appears to be a false positive — the XSUB cleanup section is not modified in this diff, and the current code already uses SAVEFREESV correctly (lines 829-834). The only actionable item is the CI workflow change which should use Automated review by Kōanae41cb2 |
Here's the summary: - **Changed `perl-actions/install-with-cpanm@v1` to `perl-actions/install-with-cpm@v2`** in `.github/workflows/test.yml` per reviewer request. The PR had accidentally switched from `cpm` to `cpanm` (a different installer tool entirely); the reviewer asked for `install-with-cpm@v2` instead. Also restored the step name from "cpanm" back to "cpm" for consistency with the other workflow jobs. - **SAVEFREESV regression (blocking review comment)**: No action needed — the current code on this branch already uses `SAVEFREESV` for exception-safe cleanup (lines 902-903, 919). This was fixed in a prior commit (`fix: resolve CI failures on garu#120`). The review comment was based on an earlier version of the diff. - **hv_ksplit no-op (suggestion)**: Already addressed in prior commit `bff3b6c` ("fix: tighten hv_clone_iterative scope and remove no-op hv_ksplit").
Rebase with requested adjustmentsBranch Changes applied
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
install-with-cpm@v1 regressed on Perl 5.8-5.22 after a recent cpm update requiring Perl 5.24+. Bump to @v2 for action-based jobs and use direct `cpm install` in the Docker-based Linux matrix (where cpm is pre-installed). Matches the approach approved by @atoomic in PR garu#120. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
install-with-cpm@v1 regressed on Perl 5.8-5.22 after a recent cpm update requiring Perl 5.24+. Bump to @v2 for action-based jobs and use direct `cpm install` in the Docker-based Linux matrix (where cpm is pre-installed). Matches the approach approved by @atoomic in PR garu#120. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
install-with-cpm@v1 regressed on Perl 5.8-5.22 after a recent cpm update requiring Perl 5.24+. Bump to @v2 for action-based jobs and use direct `cpm install` in the Docker-based Linux matrix (where cpm is pre-installed). Matches the approach approved by @atoomic in PR garu#120. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
install-with-cpm@v1 regressed on Perl 5.8-5.22 after a recent cpm update requiring Perl 5.24+. Bump to @v2 for action-based jobs and use direct `cpm install` in the Docker-based Linux matrix (where cpm is pre-installed). Matches the approach approved by @atoomic in PR garu#120. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
install-with-cpm@v1 regressed on Perl 5.8-5.22 after a recent cpm update requiring Perl 5.24+. Bump to @v2 for action-based jobs and use direct `cpm install` in the Docker-based Linux matrix (where cpm is pre-installed). Matches the approach approved by @atoomic in PR garu#120. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
install-with-cpm@v1 regressed on Perl 5.8-5.22 after a recent cpm update requiring Perl 5.24+. Bump to @v2 for action-based jobs and use direct `cpm install` in the Docker-based Linux matrix (where cpm is pre-installed). Matches the approach approved by @atoomic in PR garu#120. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
install-with-cpm@v1 regressed on Perl 5.8-5.22 after a recent cpm update requiring Perl 5.24+. Bump to @v2 for action-based jobs and use direct `cpm install` in the Docker-based Linux matrix (where cpm is pre-installed). Matches the approach approved by @atoomic in PR garu#120. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
install-with-cpm@v1 regressed on Perl 5.8-5.22 after a recent cpm update requiring Perl 5.24+. Bump to @v2 for action-based jobs and use direct `cpm install` in the Docker-based Linux matrix (where cpm is pre-installed). Matches the approach approved by @atoomic in PR garu#120. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
install-with-cpm@v1 regressed on Perl 5.8-5.22 after a recent cpm update requiring Perl 5.24+. Bump to @v2 for action-based jobs and use direct `cpm install` in the Docker-based Linux matrix (where cpm is pre-installed). Matches the approach approved by @atoomic in PR garu#120. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
install-with-cpm@v1 regressed on Perl 5.8-5.22 after a recent cpm update requiring Perl 5.24+. Bump to @v2 for action-based jobs and use direct `cpm install` in the Docker-based Linux matrix (where cpm is pre-installed). Matches the approach approved by @atoomic in PR garu#120. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
What
Make
hv_clone_iterativetruly iterative for single-entry hash chains,preventing stack overflow on deeply nested
{k=>{k=>{k=>...}}}structures.Why
hv_clone_iterativewas only iterative at the surface: it created a new HVwithout recursion, but cloned each value via
sv_clone, which calledhv_clone_iterativeagain for nested HVs. Each nesting level consumed ~2 Cstack frames. On Windows (1 MB default stack), roughly 500 additional levels
past MAX_DEPTH would crash.
This mirrors the stack overflow that
av_clone_iterative's chain-walkingoptimization already prevents for
[[[...]]]arrays.How
Add a
whileloop that detects single-key hashes whose value is an RV toanother HV, and walks the chain in-place — creating each clone HV, storing
it in
hseen, linking vianewRV_noinc, and advancing to the next level.Handles circular refs (hseen lookup), blessings (
sv_bless), and weakrefs(deferred via
weakrefsAV) at each step.When the chain ends (multi-key hash, non-HV value, or circular ref), falls
through to the existing general-case loop.
Testing
New
t/23-deep-hash-chain.twith 6 scenarios:All 330 tests pass on macOS. Platform-adaptive depths for Windows/Cygwin.
🤖 Generated with Claude Code
Quality Report
Changes: 3 files changed, 265 insertions(+), 12 deletions(-)
Code scan: clean
Tests: passed (OK)
Branch hygiene: clean
Generated by Kōan post-mission quality pipeline