4

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?

Mark Thomas
  • 37,131
  • 11
  • 74
  • 101
Josh
  • 5,631
  • 1
  • 28
  • 54

4 Answers4

8

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 # ""
steenslag
  • 79,051
  • 16
  • 138
  • 171
1

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.

Amadan
  • 191,408
  • 23
  • 240
  • 301
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
0

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).

Nakilon
  • 34,866
  • 14
  • 107
  • 142
Dan Andreatta
  • 3,611
  • 1
  • 22
  • 15