Skip Menu |

This queue is for tickets about the Net-Async-HTTP CPAN distribution.

Report information
The Basics
Id: 92728
Status: resolved
Priority: 0/
Queue: Net-Async-HTTP

People
Owner: Nobody in particular
Requestors: TEAM [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: 0.33
Fixed in: 0.34



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";
On Tue Feb 04 12:54:31 2014, TEAM wrote: Show quoted text
> 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 );
... Show quoted text
> where $head->[ON_READ] starts out as $on_read, and later returns a > coderef which uses $responder around line 473 in ::Connection.
Oh, I see where you mean now. Yeah; that's a very recent addition I added in order to solve a different bug elsewhere. Possibly I didn't implement it very well, so perhaps there's a better way to go about this which avoids the problem entirely. I tend to regard a weaken() as a last-ditch attempt to solve these kinds of cycles; usually there's a better structure. I'm pretty sure there will be one here. ((As a side-question, I'm sure there ought to be a way to get Devel::MAT to unit-test on these things, somehow. Perhaps a limited cycle-finder in only unreachable data in the difference between a before and after snapshot?)) -- Paul Evans
This was also reported as RT93232; see patch there. -- Paul Evans
Released in 0.34 -- Paul Evans