6f57bb4223
======= TimeoutInterruptSingleton created for helper methods like setup and so on. TimeoutInterrupt#timeout and TimeoutInterrupt.timeout are stubs for calling timeout of singleton-class. Scopeable ========= `TimeoutInterrupt.timeout` should be scopeable now. `TimeoutInterrupt.timeout` without args will check timeouts and will raise the next timed out timeout, needed for scopes and if many time outs at the same time occurs.
74 lines
1.7 KiB
Ruby
74 lines
1.7 KiB
Ruby
require 'ffi/libc'
|
|
require 'timeout'
|
|
|
|
module FFI
|
|
module LibC
|
|
attach_function :alarm, [:uint], :uint unless FFI::LibC.respond_to? :alarm
|
|
end
|
|
end
|
|
|
|
module TimeoutInterruptSingleton
|
|
class <<self
|
|
def timeouts thread = nil
|
|
@timeouts ||= Hash.new {|h,k| h[k] = [] }
|
|
thread = Thread.current if thread.kind_of? Thread
|
|
thread ? @timeouts[thread] : @timeouts
|
|
end
|
|
|
|
def alarm_trap sig
|
|
key, (at, bt, exception) = self.timeouts.min_by {|key,(at,bt,ex)| at }
|
|
return if Time.now < at
|
|
raise exception, 'execution expired', bt
|
|
end
|
|
|
|
def setup
|
|
if timeouts.empty?
|
|
Signal.trap( 'ALRM') {}
|
|
FFI::LibC.alarm 0
|
|
else
|
|
key, (at, bt) = timeouts.min_by {|key,(at,bt)| at }
|
|
secs = (at - Time.now)
|
|
alarm_trap 14 if 0 > secs
|
|
Signal.trap 'ALRM', &method( :alarm_trap)
|
|
FFI::LibC.alarm secs.to_i+1
|
|
end
|
|
end
|
|
|
|
def timeout seconds = nil, exception = nil
|
|
return setup if seconds.nil?
|
|
seconds = seconds.to_i
|
|
exception ||= TimeoutInterrupt::Error
|
|
raise exception, "Timeout must be longer than '0' seconds." unless 0 < seconds
|
|
unless block_given?
|
|
return lambda {|&e|
|
|
raise exception, "Expect a lambda." unless e
|
|
timeout seconds, exception, &e
|
|
}
|
|
end
|
|
at = Time.now + seconds
|
|
key, bt = Random.rand( 2**64-1), Kernel.caller
|
|
begin
|
|
self.timeouts[key] = [at, bt, exception]
|
|
setup
|
|
yield
|
|
ensure
|
|
self.timeouts.delete key
|
|
setup
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
module TimeoutInterrupt
|
|
class Error < Timeout::Error
|
|
end
|
|
|
|
def self.timeout seconds = nil, exception = nil, &e
|
|
TimeoutInterruptSingleton.timeout seconds, exception, &e
|
|
end
|
|
|
|
def timeout seconds = nil, exception = nil, &e
|
|
TimeoutInterruptSingleton.timeout seconds, exception, &e
|
|
end
|
|
end
|