Fork Child Process with Timeout and Capture Output

Fork child process with timeout and capture output

You can use IO.pipe and tell Process.spawn to use the redirected output without the need of external gem.

Of course, only starting with Ruby 1.9.2 (and I personally recommend 1.9.3)

The following is a simple implementation used by Spinach BDD internally to capture both out and err outputs:

# stdout, stderr pipes
rout, wout = IO.pipe
rerr, werr = IO.pipe

pid = Process.spawn(command, :out => wout, :err => werr)
_, status = Process.wait2(pid)

# close write ends so we could read them
wout.close
werr.close

@stdout = rout.readlines.join("\n")
@stderr = rerr.readlines.join("\n")

# dispose the read ends of the pipes
rout.close
rerr.close

@last_exit_status = status.exitstatus

The original source is in features/support/filesystem.rb

Is highly recommended you read Ruby's own Process.spawn documentation.

Hope this helps.

PS: I left the timeout implementation as homework for you ;-)

How can I timeout a forked process that might hang?

I was able to successfully kill my exec()ed process by killing the process group, as shown as the answer to question In perl, killing child and its children when child was created using open. I modified my code as follows:

my $pid = fork;
if ($pid > 0){
eval{
local $SIG{ALRM} = sub {kill 9, -$PID; die "TIMEOUT!"};
alarm $num_secs_to_timeout;
waitpid($pid, 0);
alarm 0;
};
}
elsif ($pid == 0){
setpgrp(0,0);
exec('echo blahblah | program_of_interest');
exit(0);
}

After timeout, program_of_interest is successfully killed.

Wait at most N seconds for a fork'd child PID to finish

Figured it out, thanks for all the pointers!
Edit: fixed is_pid_alive function to work if called from within a child as well.

The issue was that the parent's "watcher" thread was never completing, since os.waitpid isn't a pollable/loopable function. The solution was to remove the "watcher" thread, and instead implement a polling loop that checks a pid_is_alive() function every millisecond, like so:

def pid_is_alive(pid):
try:
os.kill(pid, 0)
os.waitpid(pid, os.WNOHANG)
os.kill(pid, 0)
except OSError:
return False
return True


def test():
X = 1000 * 1000
Y = 5000
pid = os.fork()
if pid == 0:
thread1 = MyCustomThread() #Sleeps for 30 seconds and ends.
thread1.start()
print "Started 1!"
timeout = X # say, 1000ms
while timeout > 0:
if not thread1.is_alive(): return "custom thread finished before the deadline!"
timeout -= 1
sleep(0.001)
if thread1.is_alive():
return "custom thread didn't finish before the deadline!"
thread1.stop()
exit()

else:
timeout2 = Y # say, 500ms
while timeout2 > 0:
if not pid_is_alive(pid): return "child PID finished!"
timeout2 -= 1
sleep(0.001)
if pid_is_alive(pid):
print "child PID didn't finish yet!"
exit()
print test()
print "all done!"

Waiting for child process to terminate, or not - C

Although waitpid would get you the return status of the child, its default usage would force parent to wait until the child terminates.

But your requirement (if i understood correctly) only wants parent to wait for a certain time, alarm() can be used to do that.

Then, you should use waitpid() with a specific option that returns immediately if the child has not exited yet (study the api's parameters). So if the child didn't exit, you could kill it, else you already receive its return status.

Linux: fork & execv, wait for child process hangs

I found the problem:

Within the mongoose (JSON-RPC uses mongoose) sources in the function mg_start I found the following code

#if !defined(_WIN32) && !defined(__SYMBIAN32__)
// Ignore SIGPIPE signal, so if browser cancels the request, it
// won't kill the whole process.
(void) signal(SIGPIPE, SIG_IGN);
// Also ignoring SIGCHLD to let the OS to reap zombies properly.
(void) signal(SIGCHLD, SIG_IGN);
#endif // !_WIN32

(void) signal(SIGCHLD, SIG_IGN);

causes that

if the parent does a wait(), this call will return only when all children have exited, and then returns -1 with errno set to ECHILD."

as mentioned here in the section 5.5 Voodoo: wait and SIGCHLD.

This is also described in the man page for WAIT(2)

ERRORS [...]

ECHILD [...] (This can happen for
one's own child if the action for SIGCHLD is set to SIG_IGN.
See also the Linux Notes section about threads.)

Stupid on my part not to check the return value correctly.
Before trying

if(exitedPid == workerPid) {

I should have checked that exitedPid is != -1.

If I do so errno gives me ECHILD. If I would have known that in the first place, I would have read the man page and probably found the problem faster...

Naughty of mongoose just to mess with signal handling no matter what an application wants to do about it. Additionally mongoose does not revert the altering of signal handling when being stopped with mg_stop.

Additional info:
The code that caused this problem was changed in mongoose in September 2013 with this commit.

Timeout option is not working in nodejs spawn child process

Uh, if the timeout in spawn isn't working.. you can use setTimeout logic to kill the program that would have its Time Limit Exceeded

        let python = spawn('python3', [`./uploads/${codefile}`]);
let timeout=setTimeout(()=>{
python.kill() //kills the program that is taking too long
deleteFiles(req.files)
res.status(200).json({
errorMessage: "Time Limit Exceeded, Please Optimised Your Code",
errorType: "Time Limit Exceeded"
});
},5000)

//I'd add a "python.on('end',some_callback)" but you seem to return the response on one chunk of data
python.stdout.on('data', (data) => {
res.status(200).json({ Success: data.toString() });
deleteFiles(req.files)
clearTimeout(timeout) //preventing the timeout from calling since it didn't take too long
return;
});

python.stderr.on('data', (data) => {
res.status(200).json({ Error: data.toString() });
deleteFiles(req.files)
clearTimeout(timeout) //preventing the timeout from calling since it didn't take too long
return;
});

python.on('error', (err) => {
// will handle Sigterm signal here
// res.status(200).json({
// errorMessage: "Time Limit Exceeded, Please Optimised Your Code",
// errorType: "Time Limit Exceeded"
// });
console.log(err)
clearTimeout(timeout) //preventing the timeout from calling since it didn't take too long
res.status(500).send("Internal Server Error");
deleteFiles(req.files);
return;
});

Using alarm() to implement process timeout

Most likely, in your version of OS, signal call installs handler with SA_RESTART flag set. When this flag is set, system calls are automatically restarted after signal handler.

To take control of this, instead of using obsolete and deprecated signal, use sigaction, and make sure to not specify SA_RESTART flag. This should fix your issue.



Related Topics



Leave a reply



Submit