class WEBrick::Utils::TimeoutHandler
will print ‘foo’
end
TimeoutHandler.cancel(id)
ensure
puts ‘foo’
sleep 5
begin
id = TimeoutHandler.register(10, Timeout::Error)
will raise Timeout::Error
end
TimeoutHandler.cancel(id)
ensure
puts ‘foo’
sleep 20
begin
id = TimeoutHandler.register(10, Timeout::Error)
synchronized.
Timeout handlers should be managed by using the class methods which are
Class used to manage timeout handlers across multiple threads.
#
def self.terminate
def self.terminate instance.terminate end
def cancel(thread, id)
#
def cancel(thread, id) TimeoutMutex.synchronize{ if ary = @timeout_info[thread] ary.delete_if{|info| info.object_id == id } if ary.empty? @timeout_info.delete(thread) end return true end return false } end
def initialize
Creates a new TimeoutHandler. You should use ::register and ::cancel
#
def initialize TimeoutMutex.synchronize{ @timeout_info = Hash.new } @queue = Thread::Queue.new @watcher = nil end
def interrupt(thread, id, exception)
#
def interrupt(thread, id, exception) if cancel(thread, id) && thread.alive? thread.raise(exception, "execution timeout") end end
def register(thread, time, exception)
+time+:: Timeout in seconds
Registers a new timeout handler
#
def register(thread, time, exception) info = nil TimeoutMutex.synchronize{ (@timeout_info[thread] ||= []) << (info = [time, exception]) } @queue.push nil watcher return info.object_id end
def terminate
def terminate TimeoutMutex.synchronize{ @timeout_info.clear @watcher&.kill&.join } end
def watch
def watch to_interrupt = [] while true now = Process.clock_gettime(Process::CLOCK_MONOTONIC) wakeup = nil to_interrupt.clear TimeoutMutex.synchronize{ @timeout_info.each {|thread, ary| next unless ary ary.each{|info| time, exception = *info if time < now to_interrupt.push [thread, info.object_id, exception] elsif !wakeup || time < wakeup wakeup = time end } } } to_interrupt.each {|arg| interrupt(*arg)} if !wakeup @queue.pop elsif (wakeup -= now) > 0 begin (th = Thread.start {@queue.pop}).join(wakeup) ensure th&.kill&.join end end @queue.clear end end
def watcher
def watcher (w = @watcher)&.alive? and return w # usual case TimeoutMutex.synchronize{ (w = @watcher)&.alive? and next w # pathological check @watcher = Thread.start(&method(:watch)) } end