Skip Menu |

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

Report information
The Basics
Id: 107103
Status: open
Priority: 0/
Queue: IO-Socket-IP

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

Bug Information
Severity: Critical
Broken in: 0.37
Fixed in: (no value)



CC: sri [...] cpan.org
Subject: multi-homed non-blocking connect looks broken with epoll (and kqueue, i believe)
We faced with this inside Mojolicious. Please see this topic (especially strace log): https://groups.google.com/forum/#!topic/mojolicious/RetqP4QsyHU Here you can see some description why dup2() call removes descriptor from epoll watcher: http://stackoverflow.com/questions/27948251/why-epoll-wait-doesnt-react-on-dup2writable-fd-non-writable-fd
On Tue Sep 15 09:15:29 2015, OLEG wrote: Show quoted text
> We faced with this inside Mojolicious. Please see this topic > (especially strace log): > https://groups.google.com/forum/#!topic/mojolicious/RetqP4QsyHU
Ahyes, you have stumbled upon the bug alluded to in https://metacpan.org/source/PEVANS/IO-Socket-IP-0.37/lib/IO/Socket/IP.pm#L1200 Ultimately, I'm not sure that IO::Socket::IP alone can do anything about this. It *has* to be able to switch between different socket filehandles, because it might have to swap between PF_INET and PF_INET6, and that cannot be done to an open filehandle. The failing is in the way that it is being used by the application. The application would need some way to remove and re-add the appropriate filehandle to that persistent mechanism (epoll, kqueue, etc...) each time the no-arg ->connect call is made again to request that it continue. Currently it could be done by always just removing the filehandle before that ->connect call and adding it again afterwards. I think in practice it's unlikely to ever be the case that you don't have to do that - the only thing ->connect is going to do is check for success, and lacking success it will just destroy and recreate the socket anyway. I could probably add something of that effect to the documentation. -- Paul Evans
Втр Сен 15 09:15:29 2015, OLEG писал: Show quoted text
> We faced with this inside Mojolicious. Please see this topic > (especially strace log): > https://groups.google.com/forum/#!topic/mojolicious/RetqP4QsyHU > > Here you can see some description why dup2() call removes descriptor > from epoll watcher: > http://stackoverflow.com/questions/27948251/why-epoll-wait-doesnt- > react-on-dup2writable-fd-non-writable-fd
Here is an example which confirms this problem on Linux. $ cat /etc/hosts 127.0.0.1 localhost ::1 localhost $ cat s.pl use strict; use IO::Socket::INET; my $serv = IO::Socket::INET->new(LocalPort => 1025, Listen => 10) or die $@; warn "Listening...\n"; while (my $client = $serv->accept()) { $client->syswrite("!"); } $ cat c.pl use strict; use IO::Socket::IP; use AnyEvent; use Errno qw/EINPROGRESS EWOULDBLOCK/; my $sock = IO::Socket::IP->new(PeerAddr => "localhost", PeerPort => 1025, Blocking => 0) or die $@; my $cv = AnyEvent->condvar; my $t = AnyEvent->timer( after => 5, cb => sub { warn "Timeout!"; $cv->send; } ); my $w = AnyEvent->io( fh => $sock, poll => 'w', cb => sub { if ($sock->connect) { warn "Connected!"; $cv->send; } elsif ($! != EINPROGRESS && $! != EWOULDBLOCK) { warn "Connection failed!"; $cv->send; } } ); $cv->recv; __END__ $ ./s.pl Listening... $ ./c.pl Timeout! Expected result is: "Connected!"
Втр Сен 15 09:52:24 2015, OLEG писал: Show quoted text
> Втр Сен 15 09:15:29 2015, OLEG писал:
> > We faced with this inside Mojolicious. Please see this topic > > (especially strace log): > > https://groups.google.com/forum/#!topic/mojolicious/RetqP4QsyHU > > > > Here you can see some description why dup2() call removes descriptor > > from epoll watcher: > > http://stackoverflow.com/questions/27948251/why-epoll-wait-doesnt- > > react-on-dup2writable-fd-non-writable-fd
> > > Here is an example which confirms this problem on Linux. > $ cat /etc/hosts > 127.0.0.1 localhost > ::1 localhost > > $ cat s.pl > use strict; > use IO::Socket::INET; > > my $serv = IO::Socket::INET->new(LocalPort => 1025, Listen => 10) > or die $@; > > warn "Listening...\n"; > > while (my $client = $serv->accept()) { > $client->syswrite("!"); > } > > $ cat c.pl > use strict; > use IO::Socket::IP; > use AnyEvent; > use Errno qw/EINPROGRESS EWOULDBLOCK/; > > my $sock = IO::Socket::IP->new(PeerAddr => "localhost", PeerPort => > 1025, Blocking => 0) > or die $@; > > my $cv = AnyEvent->condvar; > > my $t = AnyEvent->timer( > after => 5, > cb => sub { > warn "Timeout!"; > $cv->send; > } > ); > > > my $w = AnyEvent->io( > fh => $sock, > poll => 'w', > cb => sub { > if ($sock->connect) { > warn "Connected!"; > $cv->send; > } > elsif ($! != EINPROGRESS && $! != EWOULDBLOCK) { > warn "Connection failed!"; > $cv->send; > } > } > ); > > $cv->recv; > > __END__ > > $ ./s.pl > Listening... > > $ ./c.pl > Timeout! > > Expected result is: "Connected!"
Forgot to say: LIBEV_FLAGS=2 perl c.pl (this works - poll backend) LIBEV_FLAGS=4 perl c.pl (doesn't work - epoll backend)
On Tue Sep 15 09:52:24 2015, OLEG wrote: Show quoted text
> Here is an example which confirms this problem on Linux.
... Show quoted text
> my $w = AnyEvent->io( > fh => $sock, > poll => 'w', > cb => sub { > if ($sock->connect) { > warn "Connected!"; > $cv->send; > } > elsif ($! != EINPROGRESS && $! != EWOULDBLOCK) { > warn "Connection failed!"; > $cv->send; > } > } > );
Yes; this would be the core of the issue. You're presumably only 'add'ing the $sock FD once to the persistent (e.g. epoll) muxer once, whereas you'd need to remove/add it each time you call ->connect. I'm afraid I don't know the relevant AnyEvent'ism to arrange for that though. -- Paul Evans
Втр Сен 15 09:52:17 2015, PEVANS писал: Show quoted text
> On Tue Sep 15 09:15:29 2015, OLEG wrote:
> > We faced with this inside Mojolicious. Please see this topic > > (especially strace log): > > https://groups.google.com/forum/#!topic/mojolicious/RetqP4QsyHU
> > Ahyes, you have stumbled upon the bug alluded to in > > https://metacpan.org/source/PEVANS/IO-Socket-IP- > 0.37/lib/IO/Socket/IP.pm#L1200 > > Ultimately, I'm not sure that IO::Socket::IP alone can do anything > about this. It *has* to be able to switch between different socket > filehandles, because it might have to swap between PF_INET and > PF_INET6, and that cannot be done to an open filehandle. > > The failing is in the way that it is being used by the application. > The application would need some way to remove and re-add the > appropriate filehandle to that persistent mechanism (epoll, kqueue, > etc...) each time the no-arg ->connect call is made again to request > that it continue. > > Currently it could be done by always just removing the filehandle > before that ->connect call and adding it again afterwards. I think in > practice it's unlikely to ever be the case that you don't have to do > that - the only thing ->connect is going to do is check for success, > and lacking success it will just destroy and recreate the socket > anyway. > > I could probably add something of that effect to the documentation.
Yes, I think this needs to be documented