Timeout a Subprocess in Ruby

By Morten Møller Riis

March 30 2012 12:30 CET

Spawning of a long running process in Ruby can cause you some problems if you want to set a timeout.

For example consider the following:

               pipe = IO.popen("long_running_program")
               Process.wait pipe.pid

Now the Process.wait call will cause our Ruby script to block while waiting for it’s child to finish.

Enter Timeout – a native module in Ruby.

We can now do something like this:

               require 'timeout'
               
               begin  
                 Timeout.timeout(30) do
                  @pipe = IO.popen("long_running_program")
                  Process.wait @pipe.pid
                 end
                rescue Timeout::Error
                  Process.kill 9, @pipe.pid
                end

Better. But now we have a problem. Since we do not collect the child’s status after it finished (was killed) the kernel will hang on to that information and you’ll see the child process as a zombie (“Z” if you run ps aux).

So, we need to collect that info using Process.wait (it’ll finish immediately since the process was killed), like so:

               require 'timeout'
               
               begin  
                 Timeout.timeout(30) do
                  @pipe = IO.popen("long_running_program")
                  Process.wait @pipe.pid
                 end
                rescue Timeout::Error
                  Process.kill 9, @pipe.pid
                  Process.wait @pipe.pid # we need to collect status so it doesn't stick around as zombie process
                end

And that is a quick solution to killing off child processes via a timeout.