Waitpid Equivalent with Timeout

Waitpid equivalent with timeout?

The function can be interrupted with a signal, so you could set a timer before calling waitpid() and it will exit with an EINTR when the timer signal is raised. Edit: It should be as simple as calling alarm(5) before calling waitpid().

Is there a version of the wait() system call that sets a timeout?

There's not a wait call that takes a timeout.

What you can do instead is install a signal handler that sets a flag for SIGCHLD, and use select() to implement a timeout. select() will be interrupted by a signal.

static volatile int punt;
static void sig_handler(int sig)
{
punt = 1;
}

...

struct timeval timeout = {10,0};
int rc;

signal(SIGCHLD, sig_handler);

fork/exec stuff
//select will get interrupted by a signal

rc = select(0, NULL,NULL,NULL, &timeout );
if (rc == 0) {
// timed out
} else if (punt) {
//child terminated
}

More logic is needed if you have other signal you need to handle as well though

Killing child process after timeout, with multiple children?

I was about to recommend polling with waitpid(), using the WNOHANGflag to prevent that from blocking, but I think I came up with something better: a pipe from the child, and select() in the parent.

Specifically,

  1. the parent creates a pipe before forking.
  2. after (successfully) forking, the parent closes its copy of the write end of the pipe.
  3. the parent then uses select(), with the wanted timeout, to wait for the pipe to become available for reading. The child does not need actually to write to the pipe, or even to know about it. The write end will be closed when the child terminates, and that will make the read end available for reading (the EOF signal).
  4. If the timeout expires then the parent sends a SIGTERM to the child.
  5. Either way, the parent collects the child with waitpid().

Be sure to check the status code provided by waitpid if you care whether the child terminated normally, because there is a chance that it terminates normally between the timeout expiring and the SIGTERM being delivered, in which case I think waitpid will (properly) show it as having terminated normally. Of course, it can also terminate on account of receiving a signal from another source or for another reason -- a SIGSEGV, for example.

Here are the bones of such an approach. You'll need to add details to make it work for you, and error-handling to make it robust:

pid_t child_pid;
int term_pipe[2];

pipe(term_pipe);
child_pid = fork();

switch (child_pid) {
case -1:
// handle error
break;
case 0:
// child
// ... whatever ...
exit(0);
}
close(term_pipe[1]); // essential

fd_set read_fds;
struct timeval timeout = { /* timeout */ };
int result;

FD_ZERO(read_fds);
FD_SET(term_pipe[0], read_fds);
result = select(term_pipe[0] + 1, &read_fds, NULL, NULL, &timeout);

if (result == 0) {
// timeout
kill(child_pid, SIGTERM);
} else if (result < 0) {
// handle error
// in particular, you may need to resume waiting if the error is EINTR
} else {
// the child terminated within the timeout
assert(result == 1);
}

// don't forget to close the read end of the pipe
close(term_pipe[0]);

int status;
pid_t collected_pid = waitpid(child_pid, &status, 0);

if (collected_pid < 0) {
// handle error
} else {
assert(collected_pid == child_pid);
// ... test the status to see how the child terminated ...
// ...
}

Note that you want normal handling of child termination with this, so do not block SIGCHLD as your code presently does.

(PHP) wait for child process to exit with timeout

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.



Related Topics



Leave a reply



Submit