1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
#--
# = timeout.rb
#
# execution timeout
#
# = Copyright
#
# Copyright:: (C) 2000 Network Applied Communication Laboratory, Inc.
# Copyright:: (C) 2000 Information-technology Promotion Agency, Japan
#
#++
#
# = Description
#
# A way of performing a potentially long-running operation in a thread, and
# terminating it's execution if it hasn't finished within fixed amount of
# time.
#
# Previous versions of timeout didn't use a module for namespace. This version
# provides both Timeout.timeout, and a backwards-compatible #timeout.
#
# = Synopsis
#
# require 'timeout'
# status = Timeout::timeout(5) {
# # Something that should be interrupted if it takes too much time...
# }
#
module Timeout
##
# Raised by Timeout#timeout when the block times out.
class Error < Interrupt
end
class ExitException < ::Exception # :nodoc:
end
THIS_FILE = /\A#{Regexp.quote(__FILE__)}:/o
CALLER_OFFSET = ((c = caller[0]) && THIS_FILE =~ c) ? 1 : 0
##
# Executes the method's block. If the block execution terminates before +sec+
# seconds has passed, it returns true. If not, it terminates the execution
# and raises +exception+ (which defaults to Timeout::Error).
#
# Note that this is both a method of module Timeout, so you can 'include
# Timeout' into your classes so they have a #timeout method, as well as a
# module method, so you can call it directly as Timeout.timeout().
def timeout(sec, klass = nil)
return yield if sec == nil or sec.zero?
raise ThreadError, "timeout within critical session" if Thread.critical
exception = klass || Class.new(ExitException)
begin
x = Thread.current
y = Thread.start {
begin
sleep sec
rescue => e
x.raise e
else
x.raise exception, "execution expired" if x.alive?
end
}
yield sec
# return true
rescue exception => e
rej = /\A#{Regexp.quote(__FILE__)}:#{__LINE__-4}\z/o
(bt = e.backtrace).reject! {|m| rej =~ m}
level = -caller(CALLER_OFFSET).size
while THIS_FILE =~ bt[level]
bt.delete_at(level)
level += 1
end
raise if klass # if exception class is specified, it
# would be expected outside.
raise Error, e.message, e.backtrace
ensure
if y and y.alive?
y.kill
y.join # make sure y is dead.
end
end
end
module_function :timeout
end
##
# Identical to:
#
# Timeout::timeout(n, e, &block).
#
# Defined for backwards compatibility with earlier versions of timeout.rb, see
# Timeout#timeout.
def timeout(n, e = nil, &block) # :nodoc:
Timeout::timeout(n, e, &block)
end
##
# Another name for Timeout::Error, defined for backwards compatibility with
# earlier versions of timeout.rb.
TimeoutError = Timeout::Error # :nodoc:
if __FILE__ == $0
p timeout(5) {
45
}
p timeout(5, TimeoutError) {
45
}
p timeout(nil) {
54
}
p timeout(0) {
54
}
p timeout(5) {
loop {
p 10
sleep 1
}
}
end
|