Disabling the SIGCHLD handler

Chris Shoemaker c.shoemaker at cox.net
Mon Jan 14 00:05:18 CET 2008

On Sun, Jan 13, 2008 at 06:55:58AM +0100, Marc Lehmann wrote:
> On Sat, Jan 12, 2008 at 11:51:04AM -0500, Chris Shoemaker <c.shoemaker at cox.net> wrote:
> > Actually, ev_child is not usable by rubinius because it eagerly consumes
> > all child exit statuses, even before an ev_child has been started.
> That is untrue. It only does that when the default event loop is running,
> when you are in a watcher or outside any ev_loop call libev will do no
> such thing.

Yes, I did mean "when the default event loop is running".  I do want to use
the default event loop for ev_signals.

> > You could argue that it's the application's responsibility to catch
> > every ev_child event and store the exit status indefinitely just in
> > case it might need it in the future.
> An event-based app doesn't block and poll for the exist status of
> processes, it registers handlers for them.

Right.  I'm neither blocking nor polling.  See below.

> > However, this is very complicated and requires complex locking between
> > the reader and writer (the ev_child handler) of the data store for
> > eagerly-consumed exit statuses.
> As I explained, if for whatever reason it is inconvinient to use it,
> don't. Libev doesn't force the use of its child watcher.

Well, it doesn't force the use of ev_child, but it does force an
application to choose between either:

  a) not having access to the exit status of children that exited before a
ev_child was started, OR
  b) not being able to use ev_signal

I'd like to use ev_signal (for unrelated purposes, mostly) AND have access
to exit status of children that exited before I knew I would want their
exit status, (for which I know I need to use my old SIGCHLD handler, that
I used with libevent).

> > > I do not think so: the default loop is for use by all components in a
> > > program. If you need a loop without signal handling, just create one.
> > 
> > Actually, rubinius wants to use the default loop for ev_signal.
> But ev_signal has the same problem: As soon as some part of a program
> registers a watcher, signal handlers instaleld via sigaction stop working.

I don't follow.  I don't intend to use sigaction.  I want to use ev_signal
for all signal watching.

> > > The problem with disabling is, what should libev do when asked to provide a
> > > child watcher? abort?
> > 
> > How about treating it exactly as if the loop didn't support ev_child,
> > just like every non-default loop?
> That would mean a crash. I don't think that this is acceptable, as one of
> the basic premises of the default loop is to support all those features.

Well, I would think that aborting if the application attempted to use
a feature that it explicitly disabled would be acceptable.  However,
another option could be to offer another loop type: Something just
like the default loop but without the SIGCHLD handler.

> > Sure, a non-default loop would avoid the sigchld handler, but prevents
> > me from using ev_signal.  :(
> Other factors would, too, as signal handlers are unsharable, just like
> waiting for child exist statuses is unsharable. You'd only postpone the
> problem.

I don't follow.  What problem would I postpone?

> > I would very much like to get rid of my sigchld handler, but it has an
> > important property that I need: it does not consume the exit status of
> > any processes unless it has been explicitly asked to.
> How does it do so? Looping over all pid's on all calls to sigchld? Thats
> extremely inefficient...

No indeed.  That's not only inefficient, it's also insufficient because
I can't get the process group of the child after it has terminated, but
I would need the process group in order to know whether the child matched
the criteria that I'm interested in (in case of e.g. pid < -1 argument
to waitpid).

Instead, I loop over only list of outstanding calls to waitpid,
passing only the pid arguments that I'm interested in receiving child
statuses for.  Thus, I never receive a child status before I am ready to
consume it, so I never have to store them anywhere.

As for efficiency, this makes the SIGCHLD handler O(N) in the number
outstanding waitpid calls.  I don't expect that to be a problem,
but I'm open to suggestions for improvement.

[ After writing this, I took a look at how ev_childs are handled, and
realized that it would be quite easy to modify libev to provide the
behavior I want.  I would just remove the waitpid(-1) call, and put a
waitpid(pid) call inside the loop over childs[].  As an added benefit,
ev_child would then also support pid < -1 arguments, with the
semantics of waitpid().  I will very likely do exactly that, and I'll
send a patch if you're interested. ]

> > On another note...  I'm having problems with unreliable signal
> > delivery after a fork - with poll, select and epoll backends.
> What is "unreliable signal delivery"? libev doesn't do anything to signals
> after a fork, so whatever you see is probably normal (POSIX) behaviour or
> some bug.

I'm sorry for being unclear.  I only meant that, after a fork, signals
sent to the child don't seem to be consistently triggering the
ev_signals I would expect.  I'll need to investigate this further.  It's
quite possibly a bug somewhere in my application code.


More information about the libev mailing list