Kill Ruby code which takes too long for normal execution
gem install terminator
1 2 require 'rubygems' 3 require 'terminator'
1 2 irb(main):008:0* Terminator.terminate 3 do 3 irb(main):009:1* sleep 2 4 irb(main):010:1> puts "I will print" 5 irb(main):011:1> end 6 I will print 7 => nil 8 irb(main):012:0> 9 irb(main):013:0* Terminated
The irb session is then also terminated.
1 2 irb(main):003:0> Terminator.terminate 1 do 3 irb(main):004:1* sleep 2 4 irb(main):005:1> puts "I'll never print" 5 irb(main):006:1> end 6 Terminator::Error: 1s 7 from (irb):3 8 from (irb):3 9 irb(main):007:0>
The irb session remains active.
I tried running the script from a file however it still terminated the script prematurely if the Terminator condition was true. Here's a script I've found works for me:
1 #!/usr/bin/ruby 2 # file: test-terminator.rb 3 4 require 'rubygems' 5 require 'terminator' 6 7 class Class 8 def unsafe_methods(*methods) 9 methods.each do |method| 10 self.class_eval "def safe_and_#{method}; sandbox('#{method}'); end;" 11 end 12 end 13 end 14 15 16 class JustAClass 17 18 def initialize() 19 end 20 21 unsafe_methods :slow, :normal 22 23 def slow() 24 result = '' 25 Terminator.terminate 1 do 26 sleep 2 27 result = "I'll never print" 28 end 29 result 30 end 31 32 def normal() 33 result = '' 34 Terminator.terminate 3 do 35 sleep 2 36 result = "I will print" 37 end 38 result 39 end 40 41 def method123() 42 "well tested with local method calls which never really causes a problem" 43 end 44 45 def sandbox(statement) 46 msg = `ruby -e "require 'test-terminator'; jac = JustAClass.new; puts jac.#{statement}"` 47 msg 48 end 49 end 50 51 if __FILE__ == $0 then 52 53 jac = JustAClass.new 54 55 #jac.slow() # <- 1) this code would raise a terminator error 56 # and then terminate the script 57 58 #puts jac.normal() # <- 2) this code runs fine up until the 59 # the 3 seconds has expired at which point 60 # the script is terminated with the 61 # message 'Terminated' 62 63 #puts jac.safe_and_slow() # <- 3) executes method slow() and 64 # continues exectution 65 66 puts jac.safe_and_normal() # <- 4) executes method normal() and 67 # continues exectution 68 69 puts jac.method123() # <- a regular method which is relatively safe 70 71 puts 'now sleeping for 3 seconds' 72 sleep 3 73 puts 'normal code execution can continue' 74 end
output:
1) jac.slow()
=> ./test-terminator.rb:25:in `slow': 1s (Terminator::Error)
from ./test-terminator.rb:25
2) jac.normal()
=> I will print
well tested with local method calls which never really causes a problem
now sleeping for 3 seconds
Terminated
3) jac.safe_and_slow()
=> ./test-terminator.rb:25:in `slow': 1s (Terminator::Error)
from ./test-terminator.rb:25
well tested with local method calls which never really causes a problem
now sleeping for 3 seconds
normal code execution can continue
4) jac.safe_and_normal()
=>I will print
well tested with local method calls which never really causes a problem
now sleeping for 3 seconds
normal code execution can continue
You might be wondering why we need to manually create a ruby process surely we could just launch a new process with Thread.new right? Wrong if you read more about Ruby processes from the article Terminator - Timeout without Mercy [lindsaar.net] you will be reminded that Green threads are launched from a single Ruby process.