Issue with AnyEvent::AIO

Dominik Csapak d.csapak at proxmox.com
Fri Jun 30 08:31:14 CEST 2023


On 6/30/23 05:08, Marc Lehmann wrote:
> On Thu, Jun 29, 2023 at 03:24:54PM +0200, Dominik Csapak <d.csapak at proxmox.com> wrote:
>> We have an issue with AnyEvent::AIO, basically we don't use it directly, but as
>> soon as it's installed on a machine, our AnyEvent based daemon (a http-sever basically)
>> uses 100% cpu time on one core (per forked subprocess).
> 
> Hi!

Hi,

thanks for your prompt answer!

> 
> IF the event loop is initialised before fork (which happens during
> AnyEvent->condvar in your example, and generally in the first call), you
> must not use it after fork. In general, after fork, you need to either
> call exec or _exit (not exit!), without doing much in between.
> 
> See the FORK section in the AnyEvent manpage.
> 
> The specific reason why AnyEvent::AIO seems to make a difference is
> because it also registers an I/O watcher during AnyEvent initialisation,
> not because it does anything special.
> 
> As a demo, if you replace the "return 1" in fork_child by POSIX::_exit 1,
> the program finishes, as you don't call into AnyEvent in the child.
> 
> While some event loops (such as EV) can be made to work after fork
> even when initialized, this generally requires careful control, as you
> typically have to be certain about what watchers you want to be active
> still - it's generally not worth it to try, and in any case, very fragile.
> 
> And this is genrally no help for AnyEvent, as it has to work with other
> event loops as well.
> 
> If you need an event loop in the child, you can try to have a look at
> AnyEvent::Fork, which creates subprocesses without forking your main
> proccess.
> 
> This will also help with lots of other problems, such as graphical
> toolkits, threads and so on, which all don't support fork except in a very
> limited way.
> 

Thanks for the explanation of that, but i looked over our codebase again,
and putting the condvar before the fork was my mistake, in the actual code,
we only use it after a fork.

When doing the fork correctly and only setting up the condvar after that
in my previously posted exmpale, it works as intended
(no 100% cpu usage from those processes)

It still happens in our production code, so i guess we inadvertently trigger
some AnyEvent initialization, though i couldn't find it (yet).
Is there any way to detect that during runtime? (so i could go and try to
detect the parts of the code where that might happen.)

basically what we do is

* setup some basic things (dirs, logging, reading config, socket, etc.)
* fork our workers
  - parent: handles sigchld, reload, etc.
  - children: set up their anyevent loop with the given socket, config, etc.

Since we only use AnyEvent in the children, i am not so sure what to look for.

when calling strace on such a process i see a constant stream of

epoll_wait(4, [{events=EPOLLIN, data={u32=8, u64=4294967304}}], 64, 3346) = 1

where the timeout (3346 in this example) begins at ~5000 and goes down.
then there is a

read(3, 0x7ffd1b8e0760, 8192)           = -1 EAGAIN (Resource temporarily unavailable)

and it starts again with the epoll_wait

(sorry if that is not helpful, as i said not really that deep into anyevent)

Is there any other information i can provide to help (some log output, strace, etc.?)
I would be very grateful if you could give any hint as to how i can
debug this

Kind Regards
Dominik




More information about the anyevent mailing list