Skip Menu |

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

Report information
The Basics
Id: 129879
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.73
Fixed in: 0.74



Subject: IO::Async::Routine pipepairs are in blocking mode
As mentioned in IRC: The pipepair created in IO::Async::Routine does not seem to have any explicit step which sets up nonblocking mode. There are a few places where this might be reasonably expected to happen - https://metacpan.org/release/IO-Async/source/lib/IO/Async/Channel.pm#L397 or https://metacpan.org/release/IO-Async/source/lib/IO/Async/Channel.pm#L409 for example - but currently, the writing pipe ends up in blocking mode and so the entire loop can get stuck in a syscall leading to deadlock. An example scenario that may work as a test case: - set up a listener in the parent process - create an IO::Async::Function which pings the parent listener before completion - call the ::Function with lots of data - the parent will block trying to write the data to the child - the child will block waiting for the response from the parent listener
On Sat Jun 22 02:46:52 2019, TEAM wrote: Show quoted text
> The pipepair created in IO::Async::Routine does not seem to have any > explicit step which sets up nonblocking mode.
An initial investigation of this seems confusing, because of https://metacpan.org/release/IO-Async/source/lib/IO/Async/Loop.pm#L2380 I wonder why that code isn't being hit, or if it is it's not doing the right thing. -- Paul Evans
On Sun Jun 23 14:17:00 2019, PEVANS wrote: Show quoted text
> I wonder why that code isn't being hit, or if it is it's not doing the > right thing.
That said, that code is in the internal `__watch_io` method which is called by both Poll and Select built-in classes, and therefore Ppoll. Also I've checked the Epoll loop which also invokes it. What loop class are you using TEAM? It may be that one doesn't somehow call this. -- Paul Evans
Ahah. Of course, it turns out that Channel output streams use autoflush, and autoflush mode doesn't bother adding the underlying IO handle to the Loop unless the write first returned EAGAIN, which unless it's already _in_ blocking mode, it wouldn't do. So the fix is simple, and also I'll add a warning to IaStream in case of other similar bugs. Patch attached. -- Paul Evans
Subject: rt129879.patch
=== modified file 'lib/IO/Async/Channel.pm' --- lib/IO/Async/Channel.pm 2019-06-12 15:52:23 +0000 +++ lib/IO/Async/Channel.pm 2019-06-23 19:09:33 +0000 @@ -403,6 +403,7 @@ keys %args and croak "Unrecognised keys for setup_async_mode: " . join( ", ", keys %args ); + defined and $_->blocking( 0 ) for $self->{read_handle}, $self->{write_handle}; $self->{mode} = "async"; } === modified file 'lib/IO/Async/Stream.pm' --- lib/IO/Async/Stream.pm 2019-06-12 15:52:23 +0000 +++ lib/IO/Async/Stream.pm 2019-06-23 19:03:13 +0000 @@ -421,6 +421,11 @@ $self->can_event( "on_read" ) or croak 'Expected either an on_read callback or to be able to ->on_read'; } + + if( $self->{autoflush} and my $write_handle = $self->write_handle ) { + carp "An IO::Async::Stream with autoflush needs an O_NONBLOCK write handle" + if $write_handle->blocking; + } } sub _add_to_loop
This was released in 0.74 -- Paul Evans