libev's child reaping breaks system() function

Robin Haberkorn rh at
Mon Sep 6 17:04:40 CEST 2010

----- Original Message -----
> From: "Marc Lehmann" <schmorp at>

> Since you can install your own sigchld handler already, this doesn't
> seem
> to be so helpful?
I admit, it wouldn't be that useful, but it would be cleaner to use
an ev_signal instead of an ev_async+custom signal handler.

> > Also system() isn't broken by multithreading - it works fine as
> > long as only one thread's executing system() at a time.
> > It's the child reaping that breaks it.
> It works fine without multithreading, so how is it libev?
I also considered this - as I already mentioned. I didn't get any
feedback from the uClibc people though. But since libev's default
child-reaping side-conditions also break waitpid() calls in multithreaded
environments, I see the responsibility more on your side.

Let's summarize the problem *again*:
libev's internally reaps any child process (see childcb() in ev.c).
This results in child-reaping race conditions with any concurrent
waitpid() call, including system() calls that cannot mask out the
signals correctly.
If you don't need event-based child termination handling, you can
reset SIGCHLD signal handler (presumably to "default" since when
"ignoring" the signal, the kernel won't keep zombie-children).
But if you still want to use libev for child reaping you can't
simply use an ev_signal and do waitpid()s in its handler, e.g.
don't reap any child but only children that don't intersect with
children reaped concurrently to prevent race conditions.
The only solution I'm aware of is using a custom SIGCHLD signal
handler and activating an async watcher when the signal handler
gets invoked. This is like simulating an ev_signal.
Is this the way I'm supposed to go under these circumstances?

> So far, you have not explained how libev "breaks" system, btw. On a
> naive
> implementation, you'd simply get ECHILD, which is documented by POSIX
> for
> this case, so apparently libev doesn't break anything at all.
> > *any* other waitpid() occurrence by resulting in race
> > conditions. waitpid() is even defined as thread-safe.
> The (documented) behaviour of waitpid is not changed by libev though.
That's true, there are no segfaults. But imagine libev or some other
library would always "ignore" SIGCHLDs, maybe repeatedly to prevent
the user from fixing the handler. waitpid() wouldn't be able to
reap children anymore. This would be documented waitpid()
behaviour, too.
I would think that a library with such side-conditions, effectively
breaking a documented system call is poorly designed.

> > Making the mechanism asynchronous/non-blocking, would
> > require major code refactoring and make conceptually
> > simple code dis-proportionally harder to read.
> Seems like an empty claim - can you back it up?

Yes. Usually you're using system() for programs that
aren't expected to run long enough to cause any trouble.
That's why it may well be used in libev watcher handlers.
Now if you replace these calls with some implementation that
forks and registers an ev_child (you're still interested in
the termination status!) you'll get your result in the watcher
handler. Since your code might depend on the status, you have
to continue in that handler. This complicates otherwise
simple code because it cannot be written sequentially
Let's not talk about the problems you would have when
using that mechanism from a thread that hasn't got the
default loop (the only one supporting signal/child


------------------ managed broadband access ------------------

Travelping GmbH               phone:           +49-391-8190990
Roentgenstr. 13               fax:           +49-391-819099299
D-39108 Magdeburg             email:       info at
GERMANY                       web:

Company Registration: Amtsgericht Stendal Reg No.:   HRB 10578
Geschaeftsfuehrer: Holger Winkelmann | VAT ID No.: DE236673780

More information about the libev mailing list