summaryrefslogtreecommitdiff
path: root/lib/timeout.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/timeout.rb')
-rw-r--r--lib/timeout.rb53
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