Imagine I have a process that starts several child processes. The parent needs to know when a child exits.
I can use waitpid
, but then if/when the parent needs to exit I have no way of telling the thread that is blocked in waitpid
to exit gracefully and join it. It's nice to have things clean up themselves, but it may not be that big of a deal.
I can use waitpid
with WNOHANG
, and then sleep for some arbitrary time to prevent a busy wait. However then I can only know if a child has exited every so often. In my case it may not be super critical that I know when a child exits right away, but I'd like to know ASAP...
I can use a signal handler for SIGCHLD
, and in the signal handler do whatever I was going to do when a child exits, or send a message to a different thread to do some action. But using a signal handler obfuscates the flow of the code a little bit.
What I'd really like to do is use waitpid
on some timeout, say 5 sec. Since exiting the process isn't a time critical operation, I can lazily signal the thread to exit, while still having it blocked in waitpid
the rest of the time, always ready to react. Is there such a call in linux? Of the alternatives, which one is best?
EDIT:
Another method based on the replies would be to block SIGCHLD
in all threads with pthread
\ _sigmask()
. Then in one thread, keep calling sigtimedwait()
while looking for SIGCHLD
. This means that I can time out on that call and check whether the thread should exit, and if not, remain blocked waiting for the signal. Once a SIGCHLD
is delivered to this thread, we can react to it immediately, and in line of the wait thread, without using a signal handler.
Difference between wait and waitpid():Wait() waits for any child process but waitpid() waits for a specific child equal to pid. By default waitpid() waits for the only terminated child where as wait() waits for both terminated or a signaled child.
Define a handler for SIGCHLD that calls waitpid This allows for the possibility of SIGCHLD being raised for reasons other than the termination of a child process. ( SIGCHLD has three conventional uses: to indicate that a child process has terminated, stopped or continued.
Suspends the calling process until a child process ends or is stopped. More precisely, waitpid() suspends the calling process until the system gets status information on the child. If the system already has status information on an appropriate child when waitpid() is called, waitpid() returns immediately.
It is a blocking call that will wait till any of the child processes terminates. There are other options as well. Using the waitpid function you could wait for a specific child to terminate using its PID or you can have a non-blocking way to check if there is any child-processes that has already terminated.
Don't mix alarm()
with wait()
. You can lose error information that way.
Use the self-pipe trick. This turns any signal into a select()
able event:
int selfpipe[2]; void selfpipe_sigh(int n) { int save_errno = errno; (void)write(selfpipe[1], "",1); errno = save_errno; } void selfpipe_setup(void) { static struct sigaction act; if (pipe(selfpipe) == -1) { abort(); } fcntl(selfpipe[0],F_SETFL,fcntl(selfpipe[0],F_GETFL)|O_NONBLOCK); fcntl(selfpipe[1],F_SETFL,fcntl(selfpipe[1],F_GETFL)|O_NONBLOCK); memset(&act, 0, sizeof(act)); act.sa_handler = selfpipe_sigh; sigaction(SIGCHLD, &act, NULL); }
Then, your waitpid-like function looks like this:
int selfpipe_waitpid(void) { static char dummy[4096]; fd_set rfds; struct timeval tv; int died = 0, st; tv.tv_sec = 5; tv.tv_usec = 0; FD_ZERO(&rfds); FD_SET(selfpipe[0], &rfds); if (select(selfpipe[0]+1, &rfds, NULL, NULL, &tv) > 0) { while (read(selfpipe[0],dummy,sizeof(dummy)) > 0); while (waitpid(-1, &st, WNOHANG) != -1) died++; } return died; }
You can see in selfpipe_waitpid()
how you can control the timeout and even mix with other select()
-based IO.
Fork an intermediate child, which forks the real child and a timeout process and waits for all (both) of its children. When one exits, it'll kill the other one and exit.
pid_t intermediate_pid = fork(); if (intermediate_pid == 0) { pid_t worker_pid = fork(); if (worker_pid == 0) { do_work(); _exit(0); } pid_t timeout_pid = fork(); if (timeout_pid == 0) { sleep(timeout_time); _exit(0); } pid_t exited_pid = wait(NULL); if (exited_pid == worker_pid) { kill(timeout_pid, SIGKILL); } else { kill(worker_pid, SIGKILL); // Or something less violent if you prefer } wait(NULL); // Collect the other process _exit(0); // Or some more informative status } waitpid(intermediate_pid, 0, 0);
Surprisingly simple :)
You can even leave out the intermediate child if you're sure no other module in the program is spwaning child processes of its own.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With