Equivalent to Waitpid with a timeout?

Imagine that I have a process that starts several child processes. The parent must know when the baby will be released.

I can use waitpid , but then when / when the parent should exit, I can’t tell the thread that is blocked in waitpid to legally exit and join it. It's nice that things cleanse themselves, but it may not be that important.

I can use waitpid with WNOHANG and then sleep for some arbitrary time to prevent waiting for the wait. However, then I can only know if the child went out so often. In my case, it may not be very critical that I know when the child leaves immediately, but I would like to know as soon as possible ...

I can use the signal handler for SIGCHLD , and in the signal handler do what I was going to do when the child exits, or send a message to another thread to do some action. But using a signal handler confuses the code stream a bit.

What I really would like to do is use waitpid in some timeout, say 5 seconds. Since exiting the process is not a time-critical operation, I can lazily signal the exit of the stream, while maintaining that it is blocked in waitpid the rest of the time, always ready to respond. Is there such a call on Linux? Of the alternatives, which one is better?




EDIT:

Another answer-based method would block SIGCHLD in all threads using pthread \ _sigmask() . Then in one thread continue to call sigtimedwait() , looking for SIGCHLD . This means that I can disconnect this call and check if the thread should exit, and if not, stay blocked while waiting for the signal. As soon as a SIGCHLD delivered to this stream, we can immediately respond to it in the line of the wait stream as well, without using a signal handler.

+47
c ++ c linux
Nov 11 '08 at 21:19
source share
9 answers

A function may be interrupted by a signal, so you can set a timer before calling waitpid (), and it will exit with EINTR when the timer signal is raised. Edit: this should be as simple as calling an alarm (5) before calling waitpid ().

+6
Nov 11 '08 at 21:29
source share
β€” -

Do not mix alarm() with wait() . You may lose error information this way.

Use the trick with your own pipe. This turns any signal into a select() 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 function is similar to the following:

 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; } 

In selfpipe_waitpid() you can see how you can control the timeout and even mix with another IO select() .

+42
Nov 14 '08 at 13:08
source share

Insert an intermediate child who deploys the real child and the waiting process and expects all (both) of his children. When someone exits, he kills another and exits.

 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 exclude an intermediate child if you are sure that no other module in the program is fueling the child processes.

+29
Nov 05 2018-11-11T00:
source share

This is an interesting question. I found sigtimedwait .

EDIT 2016/08/29: Thanks for the suggestion of Mark Eddington. I tested your example on Ubuntu 16.04, it works as expected.

Note: this only works for child processes. It is a pity that on Linux / Unix there is no equivalent Window WaitForSingleObject (unrelated_process_handle, timeout) way to receive notification of an unrelated process termination during a timeout.

OK, Mark Eddington's code example here :

 /* The program creates a child process and waits for it to finish. If a timeout * elapses the child is killed. Waiting is done using sigtimedwait(). Race * condition is avoided by blocking the SIGCHLD signal before fork(). */ #include <sys/types.h> #include <sys/wait.h> #include <signal.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> static pid_t fork_child (void) { int p = fork (); if (p == -1) { perror ("fork"); exit (1); } if (p == 0) { puts ("child: sleeping..."); sleep (10); puts ("child: exiting"); exit (0); } return p; } int main (int argc, char *argv[]) { sigset_t mask; sigset_t orig_mask; struct timespec timeout; pid_t pid; sigemptyset (&mask); sigaddset (&mask, SIGCHLD); if (sigprocmask(SIG_BLOCK, &mask, &orig_mask) < 0) { perror ("sigprocmask"); return 1; } pid = fork_child (); timeout.tv_sec = 5; timeout.tv_nsec = 0; do { if (sigtimedwait(&mask, NULL, &timeout) < 0) { if (errno == EINTR) { /* Interrupted by a signal other than SIGCHLD. */ continue; } else if (errno == EAGAIN) { printf ("Timeout, killing child\n"); kill (pid, SIGKILL); } else { perror ("sigtimedwait"); return 1; } } break; } while (1); if (waitpid(pid, NULL, 0) < 0) { perror ("waitpid"); return 1; } return 0; } 
+15
Nov 24 '13 at
source share

If you intend to use the signals anyway (as suggested by Steve), you can simply send the signal manually when you want to exit. This will cause waitpid to return an EINTR, and the stream may exit. There is no need for periodic emergency / restart.

+2
Nov 12 '08 at 0:05
source share

I thought select would return EINTR when SIGCHLD would be signaled by a child. I believe this should work:

 while(1) { int retval = select(0, NULL, NULL, NULL, &tv, &mask); if (retval == -1 && errno == EINTR) // some signal { pid_t pid = (waitpid(-1, &st, WNOHANG) == 0); if (pid != 0) // some child signaled } else if (retval == 0) { // timeout break; } else // error } 

Note: you can use pselect to override the current sigmask and prevent interruptions from unnecessary signals.

+2
Oct 19 '11 at 15:44
source share

Due to the circumstances, I absolutely needed this to work in the main thread, and it was not very easy to use the trick with the recorder or eventfd, because my epoll loop worked in another thread. So I came up with this by combining other handlers. Note that in general it is much safer to do this in other ways, but it is simple. If someone wants to comment on how really bad this is, I’m all ears.

NOTE. It is absolutely imperative to block signal processing in any thread stored for the one for which you want to run this. I do this by default as I consider it to be messy for processing signals in random streams.

 static void ctlWaitPidTimeout(pid_t child, useconds_t usec, int *timedOut) { int rc = -1; static pthread_mutex_t alarmMutex = PTHREAD_MUTEX_INITIALIZER; TRACE("ctlWaitPidTimeout: waiting on %lu\n", (unsigned long) child); /** * paranoid, in case this was called twice in a row by different * threads, which could quickly turn very messy. */ pthread_mutex_lock(&alarmMutex); /* set the alarm handler */ struct sigaction alarmSigaction; struct sigaction oldSigaction; sigemptyset(&alarmSigaction.sa_mask); alarmSigaction.sa_flags = 0; alarmSigaction.sa_handler = ctlAlarmSignalHandler; sigaction(SIGALRM, &alarmSigaction, &oldSigaction); /* set alarm, because no alarm is fired when the first argument is 0, 1 is used instead */ ualarm((usec == 0) ? 1 : usec, 0); /* wait for the child we just killed */ rc = waitpid(child, NULL, 0); /* if errno == EINTR, the alarm went off, set timedOut to true */ *timedOut = (rc == -1 && errno == EINTR); /* in case we did not time out, unset the current alarm so it doesn't bother us later */ ualarm(0, 0); /* restore old signal action */ sigaction(SIGALRM, &oldSigaction, NULL); pthread_mutex_unlock(&alarmMutex); TRACE("ctlWaitPidTimeout: timeout wait done, rc = %d, error = '%s'\n", rc, (rc == -1) ? strerror(errno) : "none"); } static void ctlAlarmSignalHandler(int s) { TRACE("ctlAlarmSignalHandler: alarm occured, %d\n", s); } 

EDIT: I have since moved on to using a solution that integrates well with an existing epoll () based eventollup using timerfd. I do not lose any independence from the platform, because in any case I use epoll, and I get an extra dream, because I know that an unholy combination of multithreaded and UNIX signals will no longer harm my program.

+2
Apr 18 '13 at 13:49 on
source share

I can use the signal handler for SIGCHLD, and in the signal handler do what I'm going to do when the child leaves, or send a message to another thread to do some action. But using a signal handler confuses the code stream a bit.

To avoid race conditions, you should avoid doing anything more complicated than changing the volatile flag in the signal handler.

I think the best option in your case is to send a signal to your parents. waitpid () will then set errno to EINTR and return. At this point, you check the return value of waitpid and errno, notice that a signal has been sent to you and have taken appropriate action.

+1
Nov 11 '08 at 21:35
source share

Instead of directly calling waitpid (), you can call sigtimedwait () with SIGCHLD (which will be sent to the parent process after exiting the child process) and wait until it is delivered to the current thread, as suggested by the function name, the timeout parameter is supported .

please check the following code snippet for details

 static bool waitpid_with_timeout(pid_t pid, int timeout_ms, int* status) { sigset_t child_mask, old_mask; sigemptyset(&child_mask); sigaddset(&child_mask, SIGCHLD); if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) { printf("*** sigprocmask failed: %s\n", strerror(errno)); return false; } timespec ts; ts.tv_sec = MSEC_TO_SEC(timeout_ms); ts.tv_nsec = (timeout_ms % 1000) * 1000000; int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, NULL, &ts)); int saved_errno = errno; // Set the signals back the way they were. if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) { printf("*** sigprocmask failed: %s\n", strerror(errno)); if (ret == 0) { return false; } } if (ret == -1) { errno = saved_errno; if (errno == EAGAIN) { errno = ETIMEDOUT; } else { printf("*** sigtimedwait failed: %s\n", strerror(errno)); } return false; } pid_t child_pid = waitpid(pid, status, WNOHANG); if (child_pid != pid) { if (child_pid != -1) { printf("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid); } else { printf("*** waitpid failed: %s\n", strerror(errno)); } return false; } return true; } 

See https://android.googlesource.com/platform/frameworks/native/+/master/cmds/dumpstate/DumpstateUtil.cpp#46.

0
Jun 28 '19 at 10:51 on
source share



All Articles