Disclaimer: Absolute newbie in C, i was mostly using Java before.
In many C beginner tutorials, waitpid
is used in process management examples to wait for its child processes to finish (or have a status change using options like WUNTRACED
). However, i couldn't find any information about how to continue if no such status change occurs, either by direct user input or programmatic (e.g. timeout). So what is a good way to undo waitpid
? Something like SIGCONT
for stopped processes, but instead for processes delayed by waitpid
.
Alternatively if the idea makes no sense, it would be interesting to know why.
How about if I suggest using alarm()
? alarm()
delivers SIGALRM
after the countdown passes (See alarm()
man page for more details). But from the signals
man page, SIGALRM
default disposition is to terminate the process. So, you need to register a signal handler for handling the SIGALRM
. Code follows like this...
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void sigalrm(int signo)
{
return; // Do nothing !
}
int main()
{
struct sigaction act, oldact;
act.sa_handler = sigalrm; // Set the signal handler
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
#ifdef SA_INTERRUPT // If interrupt defined set it to prevent the auto restart of sys-call
act.sa_flags |= SA_INTERRUPT;
#endif
sigaction(SIGALRM, &act, &oldact);
pid_t fk_return = fork();
if (fk_return == 0) { // Child never returns
for( ; ; );
}
unsigned int wait_sec = 5;
alarm(wait_sec); // Request for SIGALRM
time_t start = time(NULL);
waitpid(-1, NULL, 0);
int tmp_errno = errno; // save the errno state, it may be modified in between function calls.
time_t end = time(NULL);
alarm(0); // Clear a pending alarm
sigaction(SIGALRM, &oldact, NULL);
if (tmp_errno == EINTR) {
printf("Child Timeout, waited for %d sec\n", end - start);
kill(fk_return, SIGINT);
exit(1);
}
else if (tmp_errno != 0) // Some other fatal error
exit(1);
/* Proceed further */
return 0;
}
OUTPUT
Child Timeout, waited for 5 sec
Note: You don't need to worry about SIGCHLD
because its default disposition is to ignore.
For the completeness, it is guaranteed that SIGALRM
is not delivered to the child. This is from the man page of alarm()
Alarms created by alarm() are preserved across execve(2) and are not inherited by children created via fork(2).
I don't know why it didn't strike me at first. A simple approach would be to block SIGCHLD
and call sigtimedwait()
which supports timeout option. The code goes like this...
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
sigset_t sigmask;
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &sigmask, NULL);
pid_t fk_return = fork();
if (fk_return == 0) { // Child never returns
for( ; ; );
}
if (sigtimedwait(&sigmask, NULL, &((struct timespec){5, 0})) < 0) {
if (errno == EAGAIN) {
printf("Timeout\n");
kill(fk_return, SIGINT);
exit(1);
}
}
waitpid(fk_return, NULL, 0); // Child should have terminated by now.
/* Proceed further */
return 0;
}
OUTPUT
Timeout