2

In POSIX there is no way to be notified via select/poll when a specific child process terminates. The closest you can get is a signal handler for SIGCHLD, which interacts poorly with threads, might not be available at all in an interpreted language, and wakes you up when any child process terminates. Entering the land of system-specific extensions, signalfd makes it easier to field SIGCHLD from your main event loop but doesn't completely solve either the threads problem or the interpreted-language problem, and again it wakes you up when any child process terminates; I don't know of any others that address this problem.

Does any modern incarnation of Unix provide a mechanism whereby, in an 100% race-free and thread-safe manner, you can be awakened from a call to select, poll, or similar (epoll, kqueue, ...) when a specific child process terminates?

zwol
  • 135,547
  • 38
  • 252
  • 361
  • Not an answer for the question, but if you can choose application design, you can use libevent or libev to unify processing of I/O and signal events and hide low-level details. Both of them are quite portable. – gavv Jul 17 '15 at 14:55

2 Answers2

1

Do you control or otherwise trust the children's process images, such that they can be assumed not to fiddle with an inherited resource?

Let each child inherit the write end of a pipe pair specific to that child. The parent holds the read end of every pair and knows a priori which fd is associated with each child. When a child terminates, the read end indicates a pending EOF, becoming "readable" for select/poll/etc. That should work even on ancient Unix, and also in interpreted languages where you might not be able to co-opt their native SIGCHLD handling.

Separately, you could implement what you want on a modern system with a single thread in the parent nominated to handle (or accept) all SIGCHLDs. That thread would then have to transform the reaped PIDs into some other interthread communication like I/O or a condition variable. It's not trivial, but it can be done race-free and thread-safe. (And if you are already mixing threads and fork, I infer you're taking some precautions anyway.)

Community
  • 1
  • 1
pilcrow
  • 56,591
  • 13
  • 94
  • 135
1

How can I wait for termination of specific children in a race-free manner?

Here is a list of possible options for a case when:

  • You don't control child.
  • You don't control the whole parent code (e.g. you're writing a library) and:

    • can't set global SIGCHLD handler;
    • can't fetch any signal that should be handled by other code in parent.

Preferred solution

If you are already using select() or poll(), you can just replace it with pselect() or ppoll() to wait for I/O or SIGCHLD at the same time.

If the call is interrupted with SIGCHLD, you can subsequently call non-blocking waitpid() for every monitored child.

This will work even if SA_RESTART is enabled for SIGCHLD, since (p)select() and (p)poll() are never restarted.


Other options

  1. You can do a blocking call to waitpid() or waitid() in separate thread, which would wait for specific child and then, say, write message to pipe.
  2. You can put your child process into different process groups and wait for multiple children in one call.
  3. If creating a thread for every child is not an option, you can create single thread that loops calling sigsuspend() to wait SIGCHLD (without fetching it) and then subsequently calls non-blocking waitpid() for every monitored child.

Notes

Note that using signalfd, just like setting global SIGCHLD handler, can affect other code in parent, because you can fetch signal that someone else expects to handle.

gavv
  • 4,649
  • 1
  • 23
  • 40