Richard Bucker

AnyEvent and memory leaks

Posted at — Nov 21, 2011

Recently twitter kraih posted a gist link that demonstrated a simple memory leak. I say it’s simple now because I’ve spent enough time analyzing it. To say that it’s a beginner’s error is a bit arrogant but such as it is the bug points out a bigger issue when using AnyEvent (whether inside a Mojolicious app or not) it’s own has potential to  cause memory leaks.In the example:# Very common leakmy $foo;$foo = sub { my ($i, $j) = @; return if $i >= $j; say $i++; $foo->($i, $j);};$foo->(1, 10);The bug here is caused because $foo is declared and set outside the scope of the virtual function. Then within the function the code access the variable again. That’s where the variable reference count is incremented. One way to correct this function looks something like:# Very common leakmy $foo;$foo = sub { my ($foofoo, $i, $j) = @; return if $i >= $j; say $i++; $foo->($foofoo, $i, $j); undef $foofoo; #this line is optional};$foo->($foo, 1, 10);The corrected example is nothing special but it’s not pretty (so maybe aesthetics should be #5 or #6 on the list).Where this is a problem for AnyEvent is that it uses a virtual callback function in just about every API. This means that if the function does not push the data you need into the callstack of the callback that you have to get it by triggering access to the variable though the $retval = undef;$ctx->do_somthing_cool(1, 2, 3, cb => sub { $retval = ‘done’; }).recv;The above code is going to leak memory for the same reason that the first example gives. Now there is some good and bad news. The bad news is that there is additional work to do. The good news is that it’s pretty standard and not that big of a deal. Just more $retval = undef;$retval = $ctx->do_somthing_cool(1, 2, 3, cb => sub { my $myretval = ‘done’; return $myretval}).recv; In AnyEvent, recv() returns the value in the “return” function inside the callback. One place I have not experimented with yet is what happens when you have more than one function. For example:my $retval2 = undef;my $retval2 = undef;$ctx->do_somthing_cool(1, 2, 3, cb => sub { my $myretval = ‘done’; return $myretval}); $ctx->do_somthing_cool(1, 2, 3, cb => sub { my $myretval = ‘really’; return $myretval}); [$retval1, $retval2] = $cv->recv()In this example I do not know what ->recv() is going to return and if it does return everything; what order will the results be in.The most important thing is making sure that you do not increase the number of references unnecessarily. Voila.  I’m not a beginner any more.  sniff