Josh
Josh

Reputation: 5721

How to timeout subprocess in Ruby

I want to test that a process is working so I run:

cmd = "my unix command"
results = `#{cmd}`

How can I add a timeout to the command so that if it takes more than x seconds I can assume that it is not working?

Upvotes: 4

Views: 2436

Answers (4)

Dan Andreatta
Dan Andreatta

Reputation: 3711

One caveat to the previous answers, if the child process is using sudo, then you cannot kill the child, and you will create zombie processes.

You will need to periodically run Process::waitpid(-1, Process::WNOHANG) to collect the exit status for the children and clean up the process tables (thus cleaning up the zombies).

Upvotes: 0

Much simpler way with thread:

p = Thread.new{ 
   #exec here
}
if p.join( period_in_seconds ).nil? then
   #here thread p is still working
   p.kill
else
   #here thread p completed before 'period_in_seconds' 
end

Upvotes: 0

steenslag
steenslag

Reputation: 80065

Ruby ships witrh the Timeout module.

require 'timeout'
res = ""
status = Timeout::timeout(5) {res = `#{cmd}`} rescue Timeout::Error

# a bit of experimenting:

res = nil
status = Timeout::timeout(1) {res = `sleep 2`} rescue Timeout::Error 
p res    # nil
p status # Timeout::Error

res = nil
status = Timeout::timeout(3) {res = `sleep 2`} rescue Timeout::Error 
p res    # ""
p status # ""

Upvotes: 9

Amadan
Amadan

Reputation: 198304

Put it in a thread, have another thread sleep for x seconds then kill the first one if it's not done yet.

process_thread = Thread.new do
  `sleep 6` # the command you want to run
end

timeout_thread = Thread.new do
  sleep 4   # the timeout
  if process_thread.alive?
    process_thread.kill
    $stderr.puts "Timeout"
  end
end

process_thread.join
timeout_thread.kill

steenslag's is nicer though :) This is the low-tech route.

Upvotes: 1

Related Questions