summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hv.c3
-rw-r--r--t/op/svleak.t15
2 files changed, 17 insertions, 1 deletions
diff --git a/hv.c b/hv.c
index 36b70382da..14f3399a3e 100644
--- a/hv.c
+++ b/hv.c
@@ -2370,6 +2370,7 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags)
if (entry) {
sv_setsv(key, HeSVKEY_force(entry));
SvREFCNT_dec(HeSVKEY(entry)); /* get rid of previous key */
+ HeSVKEY_set(entry, NULL);
}
else {
char *k;
@@ -2377,6 +2378,7 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags)
/* one HE per MAGICAL hash */
iter->xhv_eiter = entry = new_HE(); /* HvEITER(hv) = new_HE() */
+ HvLAZYDEL_on(hv); /* make sure entry gets freed */
Zero(entry, 1, HE);
Newxz(k, HEK_BASESIZE + sizeof(const SV *), char);
hek = (HEK*)k;
@@ -2393,6 +2395,7 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags)
Safefree(HeKEY_hek(entry));
del_HE(entry);
iter->xhv_eiter = NULL; /* HvEITER(hv) = NULL */
+ HvLAZYDEL_off(hv);
return NULL;
}
}
diff --git a/t/op/svleak.t b/t/op/svleak.t
index e6636b84c0..2323615c03 100644
--- a/t/op/svleak.t
+++ b/t/op/svleak.t
@@ -15,7 +15,7 @@ BEGIN {
use Config;
-plan tests => 28;
+plan tests => 29;
# run some code N times. If the number of SVs at the end of loop N is
# greater than (N-1)*delta at the end of loop 1, we've got a leak
@@ -186,3 +186,16 @@ SKIP: {
# [perl #114764] Attributes leak scalars
leak(2, 0, sub { eval 'my $x : shared' }, 'my $x :shared used to leak');
+
+# Tied hash iteration was leaking if the hash was freed before itera-
+# tion was over.
+package t {
+ sub TIEHASH { bless [] }
+ sub FIRSTKEY { 0 }
+}
+leak(2, 0, sub {
+ my $h = {};
+ tie %$h, t;
+ each %$h;
+ undef $h;
+}, 'tied hash iteration does not leak');