What I'm trying to do is have a child process run in the background while the parent goes and does something else. When the child returns, I'd like for the parent to get that status and do something with it. However, I don't want to explicitly wait for the child at any point.
I looked into the WNOHANG option of waitpid but this seems to be a little different than what I'm looking for. WNOHANG just only gets the status if it's done, otherwise it moves on. Ideally, I'd like some option that will not wait for the process, but will jump back and grab the return status when it's done.
Is there any way to do this?
Code example:
pid_t p = fork();
if (p == 0){
value = do_child_stuff();
return(value);
}
if (p > 0){
captureStatus(p, &status); //NOT A REAL FUNCTION
// captureStatus will put p's exit status in status
// whenever p returns, without waiting or pausing for p
//do other stuff.....
}
Is there any way to simulate the behavior of captureStatus?
You could establish a signal handler for SIGCHLD
and wait
for the process once that triggers (it will trigger when the child terminates or is killed).
However, be aware that very few useful things can be done in a signal handler. Everything must be async-signal-safe. The standard specifically mentions wait
and waitpid
as safe.
Here's the proper way (or at least one proper way) to create an asynchronous wait out of a synchronous one:
struct waitpid_async_args {
pid_t pid;
int *status;
int flags;
sem_t sem, *done;
int *err;
};
static void *waitpid_async_start(void *arg)
{
struct waitpid_async_args *a = arg;
pid_t pid = a->pid;
int *status = a->status, flags = a->flags, *err = a->err;
sem_post(&a->sem);
if (waitpid(pid, status, flags) < 0) *err = errno;
else *err = 0;
sem_post(a->done);
pthread_detach(pthread_self());
return 0;
}
int waitpid_async(pid_t pid, int *status, int flags, sem_t *done, int *err)
{
struct waitpid_async_args a = { .pid = pid, .status = status,
.flags = flags, .done = done, .err = err };
sigset_t set;
pthread_t th;
int ret;
sem_init(&a.sem, 0, 0);
sigfillset(&set);
pthread_sigmask(SIG_BLOCK, &set, &set);
ret = pthread_create(&th, 0, waidpid_async_start, &a);
if (!ret) sem_wait(&a.sem);
pthread_sigmask(SIG_SETMASK, &set, 0);
return ret;
}
Note that the asynchronous function takes as an extra argument a semaphore it will post to flag that it's done. You could just examine status
, but without a synchronization object there's no formal guarantee of memory ordering, so it's better to use an approach like this and call sem_trywait
or sem_timedwait
with a timeout to check whether the status is available yet before accessing it.
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