diff options
Diffstat (limited to 'lib/timeout.rb')
-rw-r--r-- | lib/timeout.rb | 53 |
1 files changed, 30 insertions, 23 deletions
diff --git a/lib/timeout.rb b/lib/timeout.rb index 6e7adbd259..c67a748856 100644 --- a/lib/timeout.rb +++ b/lib/timeout.rb @@ -23,29 +23,27 @@ # Copyright:: (C) 2000 Information-technology Promotion Agency, Japan module Timeout - VERSION = "0.2.0" + # The version + VERSION = "0.4.1" + + # Internal error raised to when a timeout is triggered. + class ExitException < Exception + def exception(*) # :nodoc: + self + end + end # Raised by Timeout.timeout when the block times out. class Error < RuntimeError - attr_reader :thread - - def self.catch(*args) - exc = new(*args) - exc.instance_variable_set(:@thread, Thread.current) - exc.instance_variable_set(:@catch_value, exc) - ::Kernel.catch(exc) {yield exc} - end + def self.handle_timeout(message) # :nodoc: + exc = ExitException.new(message) - def exception(*) - # TODO: use Fiber.current to see if self can be thrown - if self.thread == Thread.current - bt = caller - begin - throw(@catch_value, bt) - rescue UncaughtThrowError - end + begin + yield exc + rescue ExitException => e + raise new(message) if exc.equal?(e) + raise end - super end end @@ -62,7 +60,7 @@ module Timeout def initialize(thread, timeout, exception_class, message) @thread = thread - @deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout + @deadline = GET_TIME.call(Process::CLOCK_MONOTONIC) + timeout @exception_class = exception_class @message = message @@ -98,7 +96,7 @@ module Timeout private_constant :Request def self.create_timeout_thread - Thread.new do + watcher = Thread.new do requests = [] while true until QUEUE.empty? and !requests.empty? # wait to have at least one request @@ -109,7 +107,7 @@ module Timeout now = 0.0 QUEUE_MUTEX.synchronize do - while (now = Process.clock_gettime(Process::CLOCK_MONOTONIC)) < closest_deadline and QUEUE.empty? + while (now = GET_TIME.call(Process::CLOCK_MONOTONIC)) < closest_deadline and QUEUE.empty? CONDVAR.wait(QUEUE_MUTEX, closest_deadline - now) end end @@ -120,6 +118,10 @@ module Timeout requests.reject!(&:done?) end end + ThreadGroup::Default.add(watcher) unless watcher.group.enclosed? + watcher.name = "Timeout stdlib thread" + watcher.thread_variable_set(:"\0__detached_thread__", true) + watcher end private_class_method :create_timeout_thread @@ -132,6 +134,12 @@ module Timeout end end end + + # We keep a private reference so that time mocking libraries won't break + # Timeout. + GET_TIME = Process.method(:clock_gettime) + private_constant :GET_TIME + # :startdoc: # Perform an operation in a block, raising an error if it takes longer than @@ -185,8 +193,7 @@ module Timeout if klass perform.call(klass) else - backtrace = Error.catch(&perform) - raise Error, message, backtrace + Error.handle_timeout(message, &perform) end end module_function :timeout |