diff options
author | David Mitchell <davem@iabyn.com> | 2011-05-11 16:17:08 +0100 |
---|---|---|
committer | David Mitchell <davem@iabyn.com> | 2011-05-19 14:49:44 +0100 |
commit | 00a1a6438090b41d961c8127cc4e10d84fa70977 (patch) | |
tree | b853ea33ce4383f808a8002b90f14b1e865c7d73 | |
parent | 9c80917f6e6fa3fccba3eea4e7014931b5fa5233 (diff) | |
download | perl-00a1a6438090b41d961c8127cc4e10d84fa70977.tar.gz |
ensure hash iterator gets deleted
The recent commits to make sv_clear() iterative when freeing a hash,
introduced a bug. If the hash only has one key, and that becomes the
iterator, and is then deleted; then when the hash is freed, the LAZYDEL
feature is skipped, and the iterated hash value fails to get deleted.
The fix is simple: check for LAZYDEL before return is keys == 0.
-rw-r--r-- | hv.c | 9 | ||||
-rw-r--r-- | t/op/each.t | 19 |
2 files changed, 21 insertions, 7 deletions
@@ -1651,9 +1651,6 @@ S_hfreeentries(pTHX_ HV *hv) PERL_ARGS_ASSERT_HFREEENTRIES; - if (!((XPVHV*)SvANY(hv))->xhv_keys) - return; - while ( ((sv = Perl_hfree_next_entry(aTHX_ hv, &index))) ) { SvREFCNT_dec(sv); } @@ -1679,9 +1676,6 @@ Perl_hfree_next_entry(pTHX_ HV *hv, STRLEN *indexp) PERL_ARGS_ASSERT_HFREE_NEXT_ENTRY; - if (!((XPVHV*)SvANY(hv))->xhv_keys) - return NULL; - if (SvOOK(hv) && ((iter = HvAUX(hv))) && ((entry = iter->xhv_eiter)) ) { @@ -1697,6 +1691,9 @@ Perl_hfree_next_entry(pTHX_ HV *hv, STRLEN *indexp) iter->xhv_eiter = NULL; /* HvEITER(hv) = NULL */ } + if (!((XPVHV*)SvANY(hv))->xhv_keys) + return NULL; + array = HvARRAY(hv); assert(array); while ( ! ((entry = array[*indexp])) ) { diff --git a/t/op/each.t b/t/op/each.t index a7b128a5d7..d9e15422b2 100644 --- a/t/op/each.t +++ b/t/op/each.t @@ -6,7 +6,7 @@ BEGIN { require './test.pl'; } -plan tests => 54; +plan tests => 56; $h{'abc'} = 'ABC'; $h{'def'} = 'DEF'; @@ -238,3 +238,20 @@ for my $k (qw(each keys values)) { my @arr=%foo&&%foo; is(@arr,10,"Got expected number of elements in list context"); } +{ + # make sure a deleted active iterator gets freed timely, even if the + # hash is otherwise empty + + package Single; + + my $c = 0; + sub DESTROY { $c++ }; + + { + my %h = ("a" => bless []); + my ($k,$v) = each %h; + delete $h{$k}; + ::is($c, 0, "single key not yet freed"); + } + ::is($c, 1, "single key now freed"); +} |