class WEBrick::Utils::TimeoutHandler

Class used to manage timeout handlers across multiple threads.

Timeout handlers should be managed by using the class methods which are synchronized.

id = TimeoutHandler.register(10, Timeout::Error)
begin
  sleep 20
  puts 'foo'
ensure
  TimeoutHandler.cancel(id)
end

will raise Timeout::Error

id = TimeoutHandler.register(10, Timeout::Error)
begin
  sleep 5
  puts 'foo'
ensure
  TimeoutHandler.cancel(id)
end

will print 'foo'

Public Class Methods

cancel(id) click to toggle source

Cancels the timeout handler id

# File lib/webrick/utils.rb, line 144
def TimeoutHandler.cancel(id)
  instance.cancel(Thread.current, id)
end
new() click to toggle source

Creates a new TimeoutHandler. You should use ::register and ::cancel instead of creating the timeout handler directly.

# File lib/webrick/utils.rb, line 151
def initialize
  TimeoutMutex.synchronize{
    @timeout_info = Hash.new
  }
  @queue = Queue.new
  @watcher = Thread.start{
    to_interrupt = []
    while true
      now = Time.now
      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
register(seconds, exception) click to toggle source

Registers a new timeout handler

time

Timeout in seconds

exception

Exception to raise when timeout elapsed

# File lib/webrick/utils.rb, line 138
def TimeoutHandler.register(seconds, exception)
  instance.register(Thread.current, Time.now + seconds, exception)
end

Public Instance Methods

cancel(thread, id) click to toggle source

Cancels the timeout handler id

# File lib/webrick/utils.rb, line 215
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
interrupt(thread, id, exception) click to toggle source

Interrupts the timeout handler id and raises exception

# File lib/webrick/utils.rb, line 192
def interrupt(thread, id, exception)
  if cancel(thread, id) && thread.alive?
    thread.raise(exception, "execution timeout")
  end
end
register(thread, time, exception) click to toggle source

Registers a new timeout handler

time

Timeout in seconds

exception

Exception to raise when timeout elapsed

# File lib/webrick/utils.rb, line 203
def register(thread, time, exception)
  info = nil
  TimeoutMutex.synchronize{
    @timeout_info[thread] ||= Array.new
    @timeout_info[thread] << (info = [time, exception])
  }
  @queue.push nil
  return info.object_id
end