Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Waitpid equivalent with timeout?

Tags:

c++

c

linux

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.

like image 666
Greg Rogers Avatar asked Nov 11 '08 21:11

Greg Rogers


People also ask

What is the difference between wait () and waitpid ()?

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.

Does Waitpid use Sigchld?

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.

What does Waitpid mean?

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.

Is Waitpid a blocking call?

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.


2 Answers

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.

like image 54
geocar Avatar answered Sep 17 '22 21:09

geocar


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.

like image 45
mpartel Avatar answered Sep 19 '22 21:09

mpartel