Search code examples
phpexecwait

(PHP) wait for child process to exit with timeout


i started a child process with proc_open(), and now i need to wait until the child has finished -or- until X seconds has passed, whichever comes first. one obvious way to solve that problem is a while() loop that checks on every iteration like this:

$starttime = microtime(true);
$unused = [];
$ph = proc_open($cmd, $unused, $unused);
$terminated = false; // < protection against running pkilltree() twice
// OPTIMIZE ME: use stream_select() or something instead of sleep-loop
while (($status = proc_get_status($ph))['running']) {
    usleep(100 * 1000); // *1000 = ms
    if (! $terminated && microtime(true) - $starttime > MAX_RUNTIME_SECONDS) {
        $terminated = true;
        echo 'max runtime reached (' . MAX_RUNTIME_SECONDS . ' seconds), terminating...';
        pkilltree((int) ($status['pid']));
        // proc_terminate ( $ph, SIGKILL );
    }
}

however if the process finish in 1 millisecond, then waiting 99 milliseconds for the sleep() is (relatively) much time wasted, otoh i could just check every 1 millisecond instead of every 100, but that would waste a lot of cpu. with linux api one could probably use sigtimedwait() to wait for SIGCHLD, but in PHP i suspect (but haven't properly verified) that the PHP interpreter itself intercepts and hides SIGCHLD from userland php code, so.. any suggestions?


Solution

  • I'm just curious, is it necessary to use proc_open()? Because as your example code it is possible to do it with exec() function but it need a simple CLI tool to use with, its timeout.

    timeout - run a command with a time limit Start COMMAND, and kill it if still running after DURATION

    So this code is alternative for your code, unless you want to do something else with the child.

    $starttime = microtime(true);
    exec("timeout " . MAX_RUNTIME_SECONDS . " $cmd");
    $terminated = false;
    if (! $terminated && microtime(true) - $starttime > MAX_RUNTIME_SECONDS) {
        $terminated = true;
        echo 'max runtime reached (' . MAX_RUNTIME_SECONDS . ' seconds), terminating...';
    }
    

    This trick I've been using for years with multiprocessing in PHP. And its work perfectly for me.