summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Mitchell <davem@iabyn.com>2011-05-11 16:17:08 +0100
committerDavid Mitchell <davem@iabyn.com>2011-05-19 14:49:44 +0100
commit00a1a6438090b41d961c8127cc4e10d84fa70977 (patch)
treeb853ea33ce4383f808a8002b90f14b1e865c7d73
parent9c80917f6e6fa3fccba3eea4e7014931b5fa5233 (diff)
downloadperl-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.c9
-rw-r--r--t/op/each.t19
2 files changed, 21 insertions, 7 deletions
diff --git a/hv.c b/hv.c
index 3bd3e6e893..785a3066e0 100644
--- a/hv.c
+++ b/hv.c
@@ -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");
+}