Subject: | Cycle in ::Connection $on_read |
The attached script shows continual memory growth in 0.33 which appears to be due to the cycle on $responder in ->on_read:
my $ret = $head->[ON_READ]->( $self, $buffref, $closed, $head );
if( defined $ret ) {
return $ret if !ref $ret;
$head->[ON_READ] = $ret;
return 1;
}
where $head->[ON_READ] starts out as $on_read, and later returns a coderef which uses $responder around line 473 in ::Connection.
Weakening the local $responder ref seems valid here, and the attached patch seems to confirm this since the memory leak is now much slower (I'm guessing the remainder is due to a small scalar leaking somewhere else).
cheers,
Tom
Subject: | leaking-responder.pl |
#!/usr/bin/env perl
use strict;
use warnings;
use Net::Async::HTTP 0.32;
use Future::Utils qw(repeat);
use IO::Async::Loop::Epoll;
my $loop = IO::Async::Loop::Epoll->new;
my $ua = Net::Async::HTTP->new(
user_agent => 'NaHTTP/LeakTest',
max_connections_per_host => 0,
max_in_flight => 0,
pipeline => 0,
# stall_timeout => 30,
fail_on_error => 1,
);
$loop->add($ua);
(repeat {
my $uri = URI->new('http://tickit.perlsite.co.uk');
my $req = HTTP::Request->new(GET => $uri);
$req->header('Connection' => 'close');
$req->protocol('HTTP/1.1');
$req->header('Host' => $uri->host);
$ua->do_request(
request => $req,
# SSL_verify_mode => SSL_VERIFY_NONE
)->on_done(sub { warn "! done - @_" }
)->on_fail(sub {
my ($reason, $component, $resp, $req) = @_;
warn "FAILED: $reason - " . ($resp ? $resp->as_string("\n") : 'no resp');
});
} until => sub { shift->is_cancelled })->get;
Subject: | weak_responder.patch |
=== modified file 'lib/Net/Async/HTTP/Connection.pm'
--- lib/Net/Async/HTTP/Connection.pm 2014-01-22 20:17:07 +0000
+++ lib/Net/Async/HTTP/Connection.pm 2014-02-04 17:47:29 +0000
@@ -264,6 +264,9 @@
my $on_read = sub {
my ( $self, $buffref, $closed, $responder ) = @_;
+ # Caller holds this and we may end up returning a coderef which uses it (caller stashes
+ # this on $responder->[ON_READ]), creating a cycle.
+ Scalar::Util::weaken($responder);
if( $stall_timer ) {
$stall_reason = "receiving response header";