The following code demonstrates a server that leaks a file descriptor. We cannot figure out how to fix that. Could you please provide some guidance where the problem lies and what the correct way to deal with it is?
To understand the problem we need three things: - a mojo server with -- an asset handler -- a write callback (a subscriber to the Mojo::IOLoop::Stream write event) - a mojo client that only takes a partial response - a tool that watches the behaviour of the server Let's start with the tool. I used inotifywait to watch the open filehandles on the path and lsof to see the open filehandles on the process. Inotify shows me % inotifywait -m /var/tmp/zero --format "%T %e %f" --timefmt "%FT%T" [...] 2015-07-28T12:31:03 OPEN 2015-07-28T12:31:03 ACCESS 2015-07-28T12:31:03 ACCESS 2015-07-28T12:31:03 ACCESS and no close. lsof shows me % lsof -p 1643 | grep zero perl 1643 k 5r REG 254,8 409600 75 /var/tmp/zero The server never closes the file according to these two tools, so something in the program below must be wrong. As the downloading client we use this small tool on github: https://gist.github.com/53b164655b57deb993e1 . We call that program simply as % perl streamres.pl -s 200000 http://127.0.0.1:3000/welcome It downloads 200000 byte and then closes the connection without waiting for the end of the connection. The server side code is below. We have already identified two places that that make the server work without an FD leak. The two lines are: # [1] weaken $s; # [2] undef $s; Either of the two will do to close the FD leak. Note that the server works well when - the client downloads the full file or - there is no write_cb to unsubscribe from What is the recommended approach, when we want to count the written bytes and need to deal with clients that close their connections prematurely? % MOJO_IOLOOP_DEBUG=1 perl -Ilib -e ' use Scalar::Util qw(weaken); 0 == system dd => "if=/dev/zero", "of=/var/tmp/zero", "bs=4096", "count=100" or die; { package MyApp; use Mojo::Base "Mojolicious"; sub startup { my $self = shift; $self->routes->get("/welcome")->to("foo#hello"); $self->hook(after_build_tx => sub { my ($tx, $app) = @_; $tx->on(connection => sub { my ($tx, $connection) = @_; my $s = Mojo::IOLoop->stream($connection); # [1] weaken $s; my $sent_bytes = 0; my $write_cb = $s->on( write => sub { $sent_bytes += length $_[1]; warn "+++ Total bytes sent: $sent_bytes"; }); $tx->on(finish => sub { $s->unsubscribe(write => $write_cb); # [2] undef $s; }); }); }); } } { package MyApp::Controller::Foo; use Mojo::Base "Mojolicious::Controller"; sub hello { my $self = shift; open my $fh, "/var/tmp/zero"; my $asset = Mojo::Asset::File->new( path => "/var/tmp/zero", handle => $fh ); $self->res->content->asset($asset); $self->rendered($self->res->code); } } package main; require Mojolicious::Commands; Mojolicious::Commands->start_app("MyApp"); ' daemon -- Reactor initialized (Mojo::Reactor::EV) 100+0 records in 100+0 records out 409600 bytes (410 kB) copied, 0.000488926 s, 838 MB/s [Tue Jul 28 13:59:16 2015] [info] Listening at "http://*:3000" Server available at http://127.0.0.1:3000 -- 821cfc3bf5c6d3914d29873d6eeb2308 >>> 9004 (1) [Tue Jul 28 13:59:21 2015] [debug] Your secret passphrase needs to be changed [Tue Jul 28 13:59:21 2015] [debug] GET "/welcome" [Tue Jul 28 13:59:21 2015] [debug] Routing to controller "MyApp::Controller::Foo" and action "hello" [Tue Jul 28 13:59:21 2015] [debug] 200 OK (0.000527s, 1897.533/s) +++ Total bytes sent: 131199 at -e line 16. +++ Total bytes sent: 262271 at -e line 16. -- 821cfc3bf5c6d3914d29873d6eeb2308 <<< 9004 (0) Thank you, -- You received this message because you are subscribed to the Google Groups "Mojolicious" group. To unsubscribe from this group and stop receiving emails from it, send an email to mojolicious+unsubscr...@googlegroups.com. To post to this group, send email to mojolicious@googlegroups.com. Visit this group at http://groups.google.com/group/mojolicious. For more options, visit https://groups.google.com/d/optout.