develooper Front page | perl.perl5.porters | Postings from May 2015

Re: Premature freeing with "Non-eval closures don't need CvOUTSIDE"

Thread Previous | Thread Next
From:
Leon Timmermans
Date:
May 6, 2015 09:07
Subject:
Re: Premature freeing with "Non-eval closures don't need CvOUTSIDE"
Message ID:
CAHhgV8jrvh6VY-KZthwmBA5MpRPG6uvkpJEcQYD29R-Gka0K_w@mail.gmail.com
On Wed, May 6, 2015 at 7:31 AM, Christian Jaeger <chrjae@gmail.com> wrote:

> TLDR: coderefs captured by closures can't be weaken'ed anymore without
> being collected too early.
>
> I've started using a coding style that uses local self-recursive functions
> and at the same time returns closures to delay evaluation. Since making a
> (local) function visible to itself creates a cycle, Scalar::Util's "weaken"
> needs to be used. This by itself works fine. But when capturing the
> weakened variable in yet another closure, while this still works in v5.14.2
> it does not in v5.20.2 or bleadperl; the latter collect the reference,
> which leads to an error for attempting to call undef as a function.
>
>     sub foo {
>         my $f; $f= sub {
>             my ($n)= @_;
>             sub {
>                 if ($n > 0) {
>                     $n + &{&$f($n - 1)} # $f is undef here
>                 } else {
>                     0
>                 }
>             }
>         };
>         my $f_= $f; weaken $f;
>         &$f_;
>     }
>
>     my $res= &{foo 2};
>
>
I think weaken does exactly what it's supposed to do here.

Currently the functional-perl code base contains a hack [*] that simply
> doesn't 'weaken' code references in newer Perls, but this does leak (not
> severely enough to trigger any of functional-perl's leak tests, which are
> designed to detect other leaks, but it will definitely be a problem in some
> cases when using a functional coding style).
>
> [*]
> https://github.com/pflanze/functional-perl/commit/8afaf3ba7fa782cc6c1015fb467a9d05f74b53e8#diff-4f461f7cbffab7251d6b8a01bcf2d5b3R92
>

The usual approach is to keep a strong reference around that isn't closed
over. Another way to achieve what you want to achieve without closure
circular references is to pass $f a reference to itself as an argument,
Y-combinator style.

It seems that the purpose of the patch is to avoid retaining references to
> variables that are not used within the closure (during closure creation),
> supposedly by collecting the names of variables used within the closure's
> body at compile time and then only embed references to those into the
> closure. This is certainly a welcome change. But why does that lead to $f
> becoming undef in my test case? $f is visible, after all. Does perhaps the
> search for variables skip embedded closure bodies?
>

It's not about scope, it's about refcount. A value will be collected when
there's no hard reference to it anymore, and that's exactly what you're
causing.

Leon

Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About