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.
PERL_ARGS_ASSERT_HFREEENTRIES;
- if (!((XPVHV*)SvANY(hv))->xhv_keys)
- return;
-
while ( ((sv = Perl_hfree_next_entry(aTHX_ hv, &index))) ) {
SvREFCNT_dec(sv);
}
PERL_ARGS_ASSERT_HFREE_NEXT_ENTRY;
- if (!((XPVHV*)SvANY(hv))->xhv_keys)
- return NULL;
-
if (SvOOK(hv) && ((iter = HvAUX(hv)))
&& ((entry = iter->xhv_eiter)) )
{
iter->xhv_eiter = NULL; /* HvEITER(hv) = NULL */
}
+ if (!((XPVHV*)SvANY(hv))->xhv_keys)
+ return NULL;
+
array = HvARRAY(hv);
assert(array);
while ( ! ((entry = array[*indexp])) ) {
require './test.pl';
}
-plan tests => 54;
+plan tests => 56;
$h{'abc'} = 'ABC';
$h{'def'} = 'DEF';
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");
+}