license, tests, lib.
This commit is contained in:
parent
c3dbaa7b71
commit
9f3eca6171
3 changed files with 121 additions and 0 deletions
49
lib/timeout_interrupt.rb
Normal file
49
lib/timeout_interrupt.rb
Normal file
|
@ -0,0 +1,49 @@
|
|||
require 'ffi/libc'
|
||||
require 'timeout'
|
||||
|
||||
module FFI
|
||||
module LibC
|
||||
attach_function :alarm, [:uint], :uint unless FFI::LibC.respond_to? :alarm
|
||||
end
|
||||
end
|
||||
|
||||
module TimeoutInterrupt
|
||||
def self.timeouts
|
||||
@timeouts ||= {}
|
||||
end
|
||||
|
||||
def self.alarm_trap sig
|
||||
key, (at, bt) = TimeoutInterrupt.timeouts.min_by {|key,(at,bt)| at }
|
||||
return if Time.now < at
|
||||
raise Timeout::Error, 'execution expired', bt
|
||||
end
|
||||
|
||||
def self.setup_timeout
|
||||
if TimeoutInterrupt.timeouts.empty?
|
||||
Signal.trap( 'ALRM') {}
|
||||
FFI::LibC.alarm 0
|
||||
else
|
||||
key, (at, bt) = TimeoutInterrupt.timeouts.min_by {|key,(at,bt)| at }
|
||||
secs = (at - Time.now).to_i+1
|
||||
TimeoutInterrupt.alarm_trap if 1 > secs
|
||||
Signal.trap 'ALRM', &TimeoutInterrupt.method( :alarm_trap)
|
||||
FFI::LibC.alarm secs
|
||||
end
|
||||
end
|
||||
|
||||
def self.timeout seconds
|
||||
seconds = seconds.to_i
|
||||
raise Timeout::Error, "Timeout must be longer than '0' seconds." unless 0 < seconds
|
||||
return lambda {|&e| self.timeout seconds, &e } unless block_given?
|
||||
at = Time.now + seconds
|
||||
key, bt = Random.rand( 2**64-1), Kernel.caller
|
||||
begin
|
||||
TimeoutInterrupt.timeouts[key] = [at, bt]
|
||||
TimeoutInterrupt.setup_timeout
|
||||
yield
|
||||
ensure
|
||||
TimeoutInterrupt.timeouts.delete key
|
||||
TimeoutInterrupt.setup_timeout
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue