Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to capture the return status of a process without waiting for it?

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?

like image 462
Casey Patton Avatar asked Sep 20 '25 03:09

Casey Patton


2 Answers

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.

like image 60
cnicutar Avatar answered Sep 22 '25 20:09

cnicutar


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.

like image 22
R.. GitHub STOP HELPING ICE Avatar answered Sep 22 '25 18:09

R.. GitHub STOP HELPING ICE