Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to timeout waitpid without killing the child?

I am aware of the many questions regarding waitpid and timeouts but they all cover this by killing the child from within an alarm handler.

That is not what i want, i want to keep the process running but dispatch it from waitpid.

The underlaying problem I try to solve is a daemon process with a main loop that processes a queue. The tasks are processed one at a time.

If a task hangs the whole main loop hangs. To get around this fork() and waitpid seemed an obvious choice. Still if a task hangs the loop hangs.

I can think of workarounds where i do not use waitpid at all but i would have to track running processes another way as i still want to process one task at a time in parallel to possibly hanging tasks.

I could even kill the task but i would like to have it running to examine what exactly is going wrong. A kill handler that dumps some debug information is also possible.

Anyway, the most convenient way to solve that issue is to timeout waitpid if possble.

Edit:

This is how I used fork() and waitpid and it may be clearer what is meant by child.

my $pid = fork();

if ($pid == 0){
    # i am the child and i dont want to die
}
elsif ($pid > 0) {
    waitpid $pid, 0;
    # i am the parent and i dont want to wait longer than $timeout
    # for the child to exit
}
else {
    die "Could not fork()";
}

Edit:

Using waitpid WNOHANG does what I want. Is this usage good practice or would you do it differently?

use strict;
use warnings;
use 5.012;
use POSIX ':sys_wait_h';

my $pid = fork();

if ($pid == 0){
    say "child will sleep";
    sleep 20;
    say "child slept";
}
else {
    my $time = 10;
    my $status;
    do {
        sleep 1;
        $status = waitpid -1, WNOHANG;
        $time--;
    } while ($time && not $status );

    say "bye";
}
like image 685
matthias krull Avatar asked Dec 13 '22 09:12

matthias krull


2 Answers

If a task hangs the whole main loop hangs. To get around this fork() and waitpid seemed an obvious choice. Still if a task hangs the loop hangs.

Use waitpid with the WNOHANG option. This way it's not going to suspend the parent process and will immediately return 0 when the child has not yet exited. In your main loop you'll have to periodically poll all the children (tasks).

like image 84
Blagovest Buyukliev Avatar answered Dec 27 '22 08:12

Blagovest Buyukliev


instead of poling all the children periodically, you might want to set up a signal handler to handle SIGCHLD... from perlipc:

 use POSIX ":sys_wait_h";
    $SIG{CHLD} = sub {
        while ((my $child = waitpid(-1, WNOHANG)) > 0) {
            $Kid_Status{$child} = $?;
        }
    };
    # do something that forks...
like image 21
KURT W ANDREWS Avatar answered Dec 27 '22 06:12

KURT W ANDREWS