Skip Menu |

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

Report information
The Basics
Id: 103922
Status: resolved
Priority: 0/
Queue: IO-Async

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

Bug Information
Severity: (no value)
Broken in: 0.66
Fixed in: 0.68



Subject: Poll returning NVAL after removing handle from loop
This is (related to?) the issue that snappy raised in IRC yesterday. It seems that a combination of: * fork() - due to name resolver process, for example * IO::Async::Stream->close on a stream that's been attached to the loop leaves a closed filehandle in the poll() list: $ IO_ASYNC_LOOP=Poll strace -epoll -ff perl 2015-04-24-io-async-poll-nval.pl Process 32078 attached [pid 32077] poll([{fd=8, events=POLLIN}, {fd=5, events=POLLIN}, {fd=7, events=POLLIN}], 3, 4294967295) = 1 ([{fd=5, revents=POLLIN}]) [pid 32077] poll([{fd=8, events=POLLIN}, {fd=7, events=POLLIN}, {fd=5, events=POLLIN}, {fd=3, events=POLLOUT}], 4, 29994) = 1 ([{fd=3, revents=POLLOUT}]) IO::Async::Stream=HASH(0x203e948) at 2015-04-24-io-async-poll-nval.pl line 33. [pid 32077] poll([{fd=8, events=POLLIN}, {fd=7, events=POLLIN}, {fd=5, events=POLLIN}, {fd=3, events=POLLIN|POLLOUT}], 4, 29993) = 1 ([{fd=3, revents=POLLOUT}]) [pid 32077] poll([{fd=8, events=POLLIN}, {fd=7, events=POLLIN}, {fd=5, events=POLLIN}, {fd=3, events=0}], 4, 4294967295) = 1 ([{fd=3, revents=POLLNVAL}]) [pid 32077] poll([{fd=8, events=POLLIN}, {fd=7, events=POLLIN}, {fd=5, events=POLLIN}, {fd=3, events=0}], 4, 4294967295) = 1 ([{fd=3, revents=POLLNVAL}]) [pid 32077] poll([{fd=8, events=POLLIN}, {fd=7, events=POLLIN}, {fd=5, events=POLLIN}, {fd=3, events=0}], 4, 4294967295) = 1 ([{fd=3, revents=POLLNVAL}]) [pid 32077] poll([{fd=8, events=POLLIN}, {fd=7, events=POLLIN}, {fd=5, events=POLLIN}, {fd=3, events=0}], 4, 4294967295) = 1 ([{fd=3, revents=POLLNVAL}]) [pid 32077] poll([{fd=8, events=POLLIN}, {fd=7, events=POLLIN}, {fd=5, events=POLLIN}, {fd=3, events=0}], 4, 4294967295) = 1 ([{fd=3, revents=POLLNVAL}]) [pid 32077] poll([{fd=8, events=POLLIN}, {fd=7, events=POLLIN}, {fd=5, events=POLLIN}, {fd=3, events=0}], 4, 4294967295) = 1 ([{fd=3, revents=POLLNVAL}]) If either of those two conditions aren't met - connect via IP, for example, or not closing the handle - things seem fine. Not sure yet if this just applies to poll(), but so far it's 100% reproducible on 0.66. Note that it doesn't cause any visible problems in the attached script; this is just based on strace output. cheers, Tom
Subject: 2015-04-24-io-async-poll-nval.pl
#!/usr/bin/env perl use strict; use warnings; use IO::Async::Loop; use IO::Async::Stream; for(1..2) { my $loop = IO::Async::Loop->new; my ($conn) = $loop->connect( # host => '1.2.3.4' or addr => { ... } avoids the resolver fork, and does not # exhibit and problems. as soon as we hit a name lookup, poll() starts returning # POLLNVAL for fd3 # addr => { # ip => '127.0.0.1', # port => 80, # socktype => 'stream', # family => 'inet', # }, host => 'localhost', service => 80, socktype => 'stream', )->get; $loop->add( my $stream = IO::Async::Stream->new( handle => $conn, on_read => sub { } ) ); warn $stream; $stream->write('GET /')->get; $stream->close; }
So IO::Async::Poll ->unwatch_io never actually seems to remove entries from the $self->{pollmask} hashref. That means they'll be passed over to _poll(), and even with a mask of 0 that appears to trigger the NVAL return. Attached patch passes all the IO::Async tests and appears to resolve the issue (no more NVAL showing up in the strace output), I haven't looked into the _poll implementation to verify whether that's correct + sufficient though. cheers, Tom On 2015-04-24 02:17:41, TEAM wrote: Show quoted text
> This is (related to?) the issue that snappy raised in IRC yesterday. > > It seems that a combination of: > > * fork() - due to name resolver process, for example > * IO::Async::Stream->close on a stream that's been attached to the > loop > > leaves a closed filehandle in the poll() list: > > $ IO_ASYNC_LOOP=Poll strace -epoll -ff perl 2015-04-24-io-async-poll- > nval.pl > Process 32078 attached > [pid 32077] poll([{fd=8, events=POLLIN}, {fd=5, events=POLLIN}, {fd=7, > events=POLLIN}], 3, 4294967295) = 1 ([{fd=5, revents=POLLIN}]) > [pid 32077] poll([{fd=8, events=POLLIN}, {fd=7, events=POLLIN}, {fd=5, > events=POLLIN}, {fd=3, events=POLLOUT}], 4, 29994) = 1 ([{fd=3, > revents=POLLOUT}]) > IO::Async::Stream=HASH(0x203e948) at 2015-04-24-io-async-poll-nval.pl > line 33. > [pid 32077] poll([{fd=8, events=POLLIN}, {fd=7, events=POLLIN}, {fd=5, > events=POLLIN}, {fd=3, events=POLLIN|POLLOUT}], 4, 29993) = 1 ([{fd=3, > revents=POLLOUT}]) > [pid 32077] poll([{fd=8, events=POLLIN}, {fd=7, events=POLLIN}, {fd=5, > events=POLLIN}, {fd=3, events=0}], 4, 4294967295) = 1 ([{fd=3, > revents=POLLNVAL}]) > [pid 32077] poll([{fd=8, events=POLLIN}, {fd=7, events=POLLIN}, {fd=5, > events=POLLIN}, {fd=3, events=0}], 4, 4294967295) = 1 ([{fd=3, > revents=POLLNVAL}]) > [pid 32077] poll([{fd=8, events=POLLIN}, {fd=7, events=POLLIN}, {fd=5, > events=POLLIN}, {fd=3, events=0}], 4, 4294967295) = 1 ([{fd=3, > revents=POLLNVAL}]) > [pid 32077] poll([{fd=8, events=POLLIN}, {fd=7, events=POLLIN}, {fd=5, > events=POLLIN}, {fd=3, events=0}], 4, 4294967295) = 1 ([{fd=3, > revents=POLLNVAL}]) > [pid 32077] poll([{fd=8, events=POLLIN}, {fd=7, events=POLLIN}, {fd=5, > events=POLLIN}, {fd=3, events=0}], 4, 4294967295) = 1 ([{fd=3, > revents=POLLNVAL}]) > [pid 32077] poll([{fd=8, events=POLLIN}, {fd=7, events=POLLIN}, {fd=5, > events=POLLIN}, {fd=3, events=0}], 4, 4294967295) = 1 ([{fd=3, > revents=POLLNVAL}]) > > If either of those two conditions aren't met - connect via IP, for > example, or not closing the handle - things seem fine. > > Not sure yet if this just applies to poll(), but so far it's 100% > reproducible on 0.66. Note that it doesn't cause any visible problems > in the attached script; this is just based on strace output. > > cheers, > > Tom
Subject: 2015-04-24-io-async-poll-removal.patch
diff --git a/lib/IO/Async/Loop/Poll.pm b/lib/IO/Async/Loop/Poll.pm index 29732b9..17a963f 100644 --- a/lib/IO/Async/Loop/Poll.pm +++ b/lib/IO/Async/Loop/Poll.pm @@ -329,7 +329,7 @@ sub unwatch_io return if $mask == $curmask; $poll ? $poll->mask( $handle, $mask ) - : ( $self->{pollmask}{$fileno} = $mask ); + : ( $mask ? ($self->{pollmask}{$fileno} = $mask) : (delete $self->{pollmask}{$fileno}) ); } =head1 AUTHOR
Thanks; now applied with some formatting changes. (Basically, I've expanded out the outer ?: into a real if block). Slightly annoying that I can't work out a good way to unit-test the lack of POLLNVALs in these situations.. hmmm -- Paul Evans
Released -- Paul Evans