Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

What's the weaken here for?

by PerlOnTheWay (Monk)
on Jan 20, 2012 at 02:06 UTC ( [id://948867]=perlquestion: print w/replies, xml ) Need Help??

PerlOnTheWay has asked for the wisdom of the Perl Monks concerning the following question:

sub once { my $wrapper; $wrapper = sub { print 1; }; weaken $wrapper; return $wrapper; } my $on = once; $on->();

I don't see any difference with or without weaken $wrapper;,what's it there for?

Replies are listed 'Best First'.
Re: What's the weaken here for?
by ww (Archbishop) on Jan 20, 2012 at 02:27 UTC
Re: What's the weaken here for?
by tobyink (Canon) on Jan 20, 2012 at 08:39 UTC

    Unlike some other languages, Perl performs automatic garbage collection. Say you've defined a variable my $str = 'Hello world' then that needs to be stored in memory somewhere. When the variable goes out of scope, and Perl knows that it's not going to be used again, Perl's garbage collector can free the area of memory it was being stored in (so that the memory can be used for storing something else).

    In some cases you end up with circular references:

    my $a = []; my $b = [ $a ]; push @$a, $b; # Now $b contains a reference to $a and # $a contains a reference to $b.

    OK, that's a contrived example, but circular references do come up in real code all the time:

    sub marry { my ($husband, $wife) = @_; $husband->set_spouse($wife); $wife->set_spouse($husband); $wife->parent[0]->pay_for_party; }

    Now perhaps the $wife object contains a reference to $husband and vice versa.

    In cases like this, Perl's garbage collector perhaps considers freeing the memory used by $wife but sees that $wife is still being referred to by $husband, so it can't free that memory until $husband is destroyed. But when it considers freeing the memory used by $husband it sees that $husband is being referred to be $wife, so it can't free anything there either.

    Technically the way Perl does this is by maintaining a "reference count" for each thing that's in memory - in other words, a count of how many variables and references are pointing at it.

    This catch-22 situation prevents either of the objects being freed from memory until the program exits. In short-running programs, this is often not a problem. But for long-running processes, it results in a memory leak - the program's memory consumption grows and grows over time.

    weaken (found in the Scalar::Util package, which has been bundled with Perl seince 5.7.3) breaks the catch-22. It allows you to mark a reference as "weak" in which case it won't count towards the reference count.

    As somebody else pointed out, in the original code (but not your cut-down example), $wrapper forms a closure over itself, which is a type of circular reference. This will result in memory leaking each time once is called. (Though judging by the function name, it is presumably only called once?) Thus weaken is used to break the circularity (as far as the garbage collector is concerned), and prevent a memory leak.

Re: What's the weaken here for?
by Anonymous Monk on Jan 20, 2012 at 02:29 UTC

    I don't see any difference with or without weaken $wrapper;,what's it there for?

    Where exactly?

    How are you checking to see a difference?

    $ perl -MDevel::Peek -MScalar::Util=weaken -le " sub fudge { my $f = s +ub { print 1; }; Dump($f); weaken $f; Dump($f); $f; } $f = fudge(); $ +f->(); Dump($f);" SV = IV(0x3f8d18) at 0x3f8d1c REFCNT = 1 FLAGS = (PADMY,ROK) RV = 0x3f8cfc SV = PVCV(0x9a263c) at 0x3f8cfc REFCNT = 2 FLAGS = (PADSTALE,PADMY,ANON,WEAKOUTSIDE,CVGV_RC) COMP_STASH = 0x3f8b5c "main" START = 0x9d9178 ===> 0 ROOT = 0x9d913c GVGV::GV = 0x99a9f4 "main" :: "__ANON__" FILE = "-e" DEPTH = 0 FLAGS = 0x490 OUTSIDE_SEQ = 272 PADLIST = 0x99aa24 PADNAME = 0x99aae4(0x9a7d1c) PAD = 0x3f8d0c(0x9a7b44) OUTSIDE = 0x99aa74 (fudge) SV = IV(0x3f8d18) at 0x3f8d1c REFCNT = 1 FLAGS = (PADMY,ROK,WEAKREF) RV = 0x3f8cfc SV = PVCV(0x9a263c) at 0x3f8cfc REFCNT = 1 FLAGS = (PADSTALE,PADMY,RMG,ANON,WEAKOUTSIDE,CVGV_RC) MAGIC = 0x3fff2c MG_VIRTUAL = &PL_vtbl_backref MG_TYPE = PERL_MAGIC_backref(<) MG_OBJ = 0x3f8d1c COMP_STASH = 0x3f8b5c "main" START = 0x9d9178 ===> 0 ROOT = 0x9d913c GVGV::GV = 0x99a9f4 "main" :: "__ANON__" FILE = "-e" DEPTH = 0 FLAGS = 0x490 OUTSIDE_SEQ = 272 PADLIST = 0x99aa24 PADNAME = 0x99aae4(0x9a7d1c) PAD = 0x3f8d0c(0x9a7b44) OUTSIDE = 0x99aa74 (fudge) 1 SV = IV(0x9c2e40) at 0x9c2e44 REFCNT = 1 FLAGS = (ROK) RV = 0x3f8cfc SV = PVCV(0x9a263c) at 0x3f8cfc REFCNT = 2 FLAGS = (PADSTALE,PADMY,RMG,ANON,WEAKOUTSIDE,CVGV_RC) MAGIC = 0x3fff2c MG_VIRTUAL = &PL_vtbl_backref MG_TYPE = PERL_MAGIC_backref(<) COMP_STASH = 0x3f8b5c "main" START = 0x9d9178 ===> 0 ROOT = 0x9d913c GVGV::GV = 0x99a9f4 "main" :: "__ANON__" FILE = "-e" DEPTH = 0 FLAGS = 0x490 OUTSIDE_SEQ = 272 PADLIST = 0x99aa24 PADNAME = 0x99aae4(0x9a7d1c) PAD = 0x3f8d0c(0x9a7b44) OUTSIDE = 0x99aa74 (fudge)

    As you notice, REFCNT doesn't change :)

    Yeah, that is not a use case for weaken, you use weaken when you have circular references, say

    my $foo = { kid => { } }; $foo->{kid}{parent} = $foo; weaken $foo->{kid}{parent};
        The author must have done this for some reason...

        Because in the real code, unlike your simplification of it, the weakened sub reference forms a closure over itself:

        $wrapper = sub { $self->unsubscribe($name => $wrapper); $cb->(@_); };

        Ie. It contains a circular reference which would leak memory without the weakening.

        For example, with the weaken commented, this leaks like a sieve:

        #! perl -slw use strict; use Scalar::Util qw[ weaken ]; sub once { my $wrapper; $wrapper = sub { return $wrapper->(); }; # weaken $wrapper; return $wrapper; } once for 1 .. 1e9;

        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        The start of some sanity?

        The actual code is the sub once of Mojo::EventEmitter

        The author must have done this for some reason...

        Yeah, well Mr. Sherlock, that code is different than what you posted. The reason is always the same, circular references prevent release of memory

        { my $foo = {}; # ref count 1 $foo->{foo} = $foo; # ref count 2 } ### $foo goes out of scope here, memory should be released to perl # here, $foo is not addressable (you can't refer to it by $foo), # but its memory is not released until exit # because there is still a reference to $foo # it refers to it self, its self-referential, a circular reference # for memory held by $foo to be released after $foo goes out of scope # $foo->{foo} has to be weakened, the reference count decreased

        The actual code, which you should have copy/pasted to get a reasonable answer, since editing code you don't understand changes its meaning

        sub once { my ($self, $name, $cb) = @_; weaken $self; my $wrapper; $wrapper = sub { # a special kind of circular reference # CLOSURE, $wrapper keeps $self alive after return $wrapper (REFCNT+1) # $wrapper refers to itself, CIRCULAR REFERENCE $self->unsubscribe($name => $wrapper); $cb->(@_); }; $self->on($name => $wrapper); # potential CIRCULAR REFERENCE (on pro +bably also stores $wrapper, but it probably also uses weaken ) weaken $wrapper; return $wrapper; }

        How do I post a question effectively? says, when reducing code size, reduce to the essence, don't change the essence :)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://948867]
Approved by ww
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (3)
As of 2024-04-19 19:44 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found