This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Free iterator when freeing tied hash
authorFather Chrysostomos <sprout@cpan.org>
Sat, 22 Sep 2012 05:01:19 +0000 (22:01 -0700)
committerFather Chrysostomos <sprout@cpan.org>
Sun, 23 Sep 2012 00:10:43 +0000 (17:10 -0700)
The current iterator was leaking when a tied hash was freed or
undefined.

Since we already have a mechanism, namely HvLAZYDEL, for freeing
HvEITER when not referenced elsewhere, we can use that.

hv.c
t/op/svleak.t

diff --git a/hv.c b/hv.c
index 36b7038..14f3399 100644 (file)
--- 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;
         }
     }
index e6636b8..2323615 100644 (file)
@@ -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');