aboutsummaryrefslogtreecommitdiffstats
path: root/lib/webrick/utils.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/webrick/utils.rb')
-rw-r--r--lib/webrick/utils.rb75
1 files changed, 75 insertions, 0 deletions
diff --git a/lib/webrick/utils.rb b/lib/webrick/utils.rb
index cf9da6f2ce..4a447aaf85 100644
--- a/lib/webrick/utils.rb
+++ b/lib/webrick/utils.rb
@@ -96,5 +96,80 @@ module WEBrick
end
module_function :random_string
+ ###########
+
+ require "thread"
+ require "timeout"
+ require "singleton"
+
+ class TimeoutHandler
+ include Singleton
+ TimeoutMutex = Mutex.new
+
+ def TimeoutHandler.register(seconds, exception)
+ TimeoutMutex.synchronize{
+ instance.register(Thread.current, Time.now + seconds, exception)
+ }
+ end
+
+ def TimeoutHandler.cancel(id)
+ TimeoutMutex.synchronize{
+ instance.cancel(Thread.current, id)
+ }
+ end
+
+ def initialize
+ @timeout_info = Hash.new
+ Thread.start{
+ while true
+ now = Time.now
+ @timeout_info.each{|thread, ary|
+ ary.each{|info|
+ time, exception = *info
+ interrupt(thread, info.object_id, exception) if time < now
+ }
+ }
+ sleep 0.5
+ end
+ }
+ end
+
+ def interrupt(thread, id, exception)
+ TimeoutMutex.synchronize{
+ if cancel(thread, id) && thread.alive?
+ thread.raise(exception, "execution timeout")
+ end
+ }
+ end
+
+ def register(thread, time, exception)
+ @timeout_info[thread] ||= Array.new
+ @timeout_info[thread] << [time, exception]
+ return @timeout_info[thread].last.object_id
+ end
+
+ def cancel(thread, id)
+ 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
+ end
+
+ def timeout(seconds, exception=Timeout::Error)
+ return yield if seconds.nil? or seconds.zero?
+ raise ThreadError, "timeout within critical session" if Thread.critical
+ id = TimeoutHandler.register(seconds, exception)
+ begin
+ yield(seconds)
+ ensure
+ TimeoutHandler.cancel(id)
+ end
+ end
+ module_function :timeout
end
end