Event-polling after a fork

Marc Lehmann schmorp at schmorp.de
Fri Feb 28 22:24:26 CET 2020


On Fri, Feb 28, 2020 at 02:10:15PM -0500, Felipe Gasper <felipe at felipegasper.com> wrote:
> > need resetting, and those that don't, so it would break otherwise
> > correctly-written programs. In other words, no, it doesn't "work", by
> > design.
> 
> If I take your meaning, you’re saying that, for example, epoll would break because after a fork the two processes share the same epoll. That seems solvable enough in a backend that’s aware it uses epoll rather than select/poll: just make that implementation of reset() close the epoll rather than removing individual watchers from the epoll.

Epoll is one problem. But other modules registering watchers would also
break (because their watchers would stop working) - and while you might be
able to control every bit of code in your program by not using any modules
you didn't personally write, that's not a problem that AnyEvent can or
should solve:

Either you control everything, then you can do whatever you wish with the
event loop of your choice (such as finding a way to "reset" or ignore
watchers, such as calling EV::default_destroy - since you must be aware
of everything, it's not a problem to call undocumented or unsupported
functions, because the EV version you use is yours, and you verified that
it works and never upgrade without checking first). And if things breask,
you get to keep the pieces.

Or you don't control everything, then any way of "reset" is not the
solution for your problem, because you don't know what the code that you
don't control does, and on which watchers it relies, or not, and how it
handles them.

And with fork, it's not just the event loop - once you use IO::AIO or Glib
or any module that uses threads (evem perl's emulated processes a.k.a.
ithreads), you cannot fork from perl anymore, as it invokes undefined
behaviour. Even if you could fork, you wouldn't be able to continue doing
anything in the child, as fork destroys threads without a way to recover
from that.

And that's just the tip of the iceberg.

That doesn't mean that there is no way to do it, for example, you could
say your propgram is Linux/glibc-only and rely on its extensions. And you
could require specific versions of any modules you use. And you could
verify that those specific versions have the means to somehow recover (for
example, IO::AIO has a reinit function which *can* *sometimes* be used
under *specific* circumstances to continue in the child, *iff* runing on
linux etc.).

But it means that, unless you are prepared to control everything in your
process - every module, every C library down to the libc, trying to use
fork in the way you seem to want to use it is futile - it's not goijng to
work portably, and it likely won't worik stably - it's simply a design bug
waiting to cause an accident.

That's why AnyEvent::Fork does its thing using separate processes, and
thats why it uses Proc::FastSpawn, which allows it to fork despite these
problems.

These modules are not mandatory, but simply calling fork, doing some dirty
hacks and praying it works well enough is not something that is useful to
support in a library such as AnyEvent, which exists to allow well-written
programs (and modules) to work well, not support hacks that can't be made
to work reliable.

> By “work”, I mean simply that the child does evented I/O independently of the parent.

Yeah, that's the problem of "simple" - you call it simple, but I have thought
about this for years, and haven't found or seen anything that would achieve
that, certainly not in a "simple" way.

Certainly "reset" does _not_ achieve "independent of the parent evented
I/O", and that is exactly what I am pointing out with this being a XY
problem: your assumed solution doesn't actually solve the problem, i.e.
even if what you propose as solution were implemented, it wouldn't
actually solve your problem.

If you know of a "simple" way to make it work, I am all ears, but since
you seem to think that resetting watchers does it, I doubt you know of a
simple way, either - I am pretty convinved there is no simple way, outside
the very simple use cases I outlined before, such as not initialising
AnyEvent or not loading other modules before fork.

-- 
                The choice of a       Deliantra, the free code+content MORPG
      -----==-     _GNU_              http://www.deliantra.net
      ----==-- _       generation
      ---==---(_)__  __ ____  __      Marc Lehmann
      --==---/ / _ \/ // /\ \/ /      schmorp at schmorp.de
      -=====/_/_//_/\_,_/ /_/\_\



More information about the anyevent mailing list