Skip Menu |

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

Report information
The Basics
Id: 104130
Status: patched
Priority: 0/
Queue: IO-Async

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

Bug Information
Severity: Wishlist
Broken in: (no value)
Fixed in: (no value)



Subject: Loops should survive forks
Sometimes we want to use the event loop after a fork. The simplest case I've found is a pre-forking NaHTTP::Server, in which I create the listening socket in the parent, then fork, and have each child ->listen on it. To share memory, I create all the objects, including the loop, in the parent. If I'm using the Epoll loop, the whole thing breaks because all children are adding fds to the *same* epoll instance. I have worked around the issue by calling this function: sub _fix_the_loop { my ($loop) = @_; require Linux::Epoll; # let's make sure that each child has a separate epoll instance $loop->{epoll} = Linux::Epoll->new; # then we want to re-add all the file descriptors that were # registered in the parent's loop my $watches = $loop->{iowatches}; return unless $watches && ref($watches); # $watches is a hashref keyed on file descriptors: we don't # actually care about the keys for my $watch (values %$watches) { my %args; # this unpacks $watch the same way as # IO::Async::Loop::__watch_io (yes, I'm depending on an # implementation detail, so sue me) @args{qw(handle on_read_ready on_write_ready on_hangup)} = @$watch; # finally we re-add the watch to the loop $loop->watch_io(%args); } return; } IO::Async::Loop should have a 'post_fork' empty method, and IaL::Epoll should override it with something like my function.
On Thu Apr 30 11:40:50 2015, DAKKAR wrote: Show quoted text
> IO::Async::Loop should have a 'post_fork' empty method, and IaL::Epoll > should override it with something like my function.
Added; see patch. I also wonder to what extent this could be detected automatically. For example, it would be easy enough to store $self->{pid} = $$ in constructor, and then if( $self->{pid} != $$ ) { $self->post_fork; $self->{pid} = $$; } as part of the main loop body. But that might be too much magic? Alternatively, as it's only a problem in specialised cases like Epoll, I wonder if it could be handled there instead. 14:39 <LeoNerd> leont: epoll is Linux-specific. Linux has the rediculously-fast syscall gate stuff to handle some fast syscalls, of which getpid() is one. So a *C* function calling getpid() is really really cheap 14:40 <LeoNerd> I wonder if you'd consider adding a set of functions to Linux::Epoll that all bomb out with some special error value / semantic if getpid() doesn't match its expectations 14:40 <LeoNerd> I'm thinking a pair of epoll_ctl_pidcheck() and epoll_pwait_pidcheck() 14:40 <LeoNerd> Then there'd be zero extra cost in perl-land for the common case of a matching PID 14:41 <leont> Maybe 14:41 <leont> I'd make that a constructor argument instead of a separate method I think 14:41 <LeoNerd> Ah perhaps 14:41 <leont> Have a hard time imagining someone wanting that part-timer, it's either always or never 14:41 <LeoNerd> Ahyes, true 14:42 <LeoNerd> I forget quite what the interface looks like - if you have more object state visible in the XS layer then yes, that would be neater 14:44 <LeoNerd> I don't see any errno values that would be sensible here though Some thoughts to ponder on... -- Paul Evans
Subject: rt104130.patch
=== modified file 'lib/IO/Async/Loop.pm' --- lib/IO/Async/Loop.pm 2015-04-17 19:43:46 +0000 +++ lib/IO/Async/Loop.pm 2015-06-01 13:54:09 +0000 @@ -558,6 +558,25 @@ $self->stop; } +=head2 $loop->post_fork + +The base implementation of this method does nothing. It is provided in case +some Loop subclasses should take special measures after a C<fork()> system +call if the main body of the program should survive in both running processes. + +This may be required, for example, in a long-running server daemon that forks +multiple copies on startup after opening initial listening sockets. A loop +implementation that uses some in-kernel resource that becomes shared after +forking (for example, a Linux C<epoll> or a BSD C<kqueue> filehandle) would +need recreating in the new child process before the program can continue. + +=cut + +sub post_fork +{ + # empty +} + ########### # Futures # ###########
This patch itself is now released. However, various other loops (e.g. Epoll) may still want some updating to actually make use of it. -- Paul Evans