diff options
author | Dave Mitchell <davem@fdisolutions.com> | 2002-12-10 01:26:44 +0000 |
---|---|---|
committer | Rafael Garcia-Suarez <rgarciasuarez@gmail.com> | 2002-12-14 22:34:25 +0000 |
commit | 7dafbf5232bace07a044625a5a956b73da3928d5 (patch) | |
tree | 5042129d851b4706c4c22a7c67de6b3092bc08cb /cv.h | |
parent | 0412d5267fa2300f66eb4eb554e2af493b5e5b33 (diff) | |
download | perl-7dafbf5232bace07a044625a5a956b73da3928d5.tar.gz |
Proper fix for CvOUTSIDE weak refcounting
Message-ID: <20021210012644.A7843@fdgroup.com>
p4raw-id: //depot/perl@18302
Diffstat (limited to 'cv.h')
-rw-r--r-- | cv.h | 61 |
1 files changed, 61 insertions, 0 deletions
@@ -81,6 +81,8 @@ Returns the stash of the CV. #define CVf_LOCKED 0x0080 /* CV locks itself or first arg on entry */ #define CVf_LVALUE 0x0100 /* CV return value can be used as lvalue */ #define CVf_CONST 0x0200 /* inlinable sub */ +#define CVf_WEAKOUTSIDE 0x0400 /* CvOUTSIDE isn't ref counted */ + /* This symbol for optimised communication between toke.c and op.c: */ #define CVf_BUILTIN_ATTRS (CVf_METHOD|CVf_LOCKED|CVf_LVALUE) @@ -135,3 +137,62 @@ Returns the stash of the CV. #define CvCONST_on(cv) (CvFLAGS(cv) |= CVf_CONST) #define CvCONST_off(cv) (CvFLAGS(cv) &= ~CVf_CONST) +#define CvWEAKOUTSIDE(cv) (CvFLAGS(cv) & CVf_WEAKOUTSIDE) +#define CvWEAKOUTSIDE_on(cv) (CvFLAGS(cv) |= CVf_WEAKOUTSIDE) +#define CvWEAKOUTSIDE_off(cv) (CvFLAGS(cv) &= ~CVf_WEAKOUTSIDE) + + +/* +=head1 CV reference counts and CvOUTSIDE + +=for apidoc m|bool|CvWEAKOUTSIDE|CV *cv + +Each CV has a pointer, C<CvOUTSIDE()>, to its lexically enclosing +CV (if any). Because pointers to anonymous sub prototypes are +stored in C<&> pad slots, it is a possible to get a circular reference, +with the parent pointing to the child and vice-versa. To avoid the +ensuing memory leak, we do not increment the reference count of the CV +pointed to by C<CvOUTSIDE> in the I<one specific instance> that the parent +has a C<&> pad slot pointing back to us. In this case, we set the +C<CvWEAKOUTSIDE> flag in the child. This allows us to determine under what +circumstances we should decrement the refcount of the parent when freeing +the child. + +There is a further complication with non-closure anonymous subs (ie those +that do not refer to any lexicals outside that sub). In this case, the +anonymous prototype is shared rather than being cloned. This has the +consequence that the parent may be freed while there are still active +children, eg + + BEGIN { $a = sub { eval '$x' } } + +In this case, the BEGIN is freed immediately after execution since there +are no active references to it: the anon sub prototype has +C<CvWEAKOUTSIDE> set since it's not a closure, and $a points to the same +CV, so it doesn't contribute to BEGIN's refcount either. When $a is +executed, the C<eval '$x'> causes the chain of C<CvOUTSIDE>s to be followed, +and the freed BEGIN is accessed. + +To avoid this, whenever a CV and its associated pad is freed, any +C<&> entries in the pad are explicitly removed from the pad, and if the +refcount of the pointed-to anon sub is still positive, then that +child's C<CvOUTSIDE> is set to point to its grandparent. This will only +occur in the single specific case of a non-closure anon prototype +having one or more active references (such as C<$a> above). + +One other thing to consider is that a CV may be merely undefined +rather than freed, eg C<undef &foo>. In this case, its refcount may +not have reached zero, but we still delete its pad and its C<CvROOT> etc. +Since various children may still have their C<CvOUTSIDE> pointing at this +undefined CV, we keep its own C<CvOUTSIDE> for the time being, so that +the chain of lexical scopes is unbroken. For example, the following +should print 123: + + my $x = 123; + sub tmp { sub { eval '$x' } } + my $a = tmp(); + undef &tmp; + print $a->(); + +=cut +*/ |