From 354cd6f210c966327b1adffc0b81990827b77a0d Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Sun, 15 May 2022 13:43:29 +0200 Subject: [PATCH] [ruby/timeout] Handle Timeout + fork and add test for it https://github.com/ruby/timeout/commit/4baee63b9b --- lib/timeout.rb | 51 +++++++++++++++++++++++++------------------- test/test_timeout.rb | 20 +++++++++++++++++ 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/lib/timeout.rb b/lib/timeout.rb index 3c3cb8e066..414983088c 100644 --- a/lib/timeout.rb +++ b/lib/timeout.rb @@ -95,31 +95,38 @@ module Timeout end private_constant :Request - def self.ensure_timeout_thread_created - unless @timeout_thread - TIMEOUT_THREAD_MUTEX.synchronize do - @timeout_thread ||= Thread.new do - requests = [] - while true - until QUEUE.empty? and !requests.empty? # wait to have at least one request - req = QUEUE.pop - requests << req unless req.done? - end - closest_deadline = requests.min_by(&:deadline).deadline + def self.create_timeout_thread + Thread.new do + requests = [] + while true + until QUEUE.empty? and !requests.empty? # wait to have at least one request + req = QUEUE.pop + requests << req unless req.done? + end + closest_deadline = requests.min_by(&:deadline).deadline - now = 0.0 - QUEUE_MUTEX.synchronize do - while (now = Process.clock_gettime(Process::CLOCK_MONOTONIC)) < closest_deadline and QUEUE.empty? - CONDVAR.wait(QUEUE_MUTEX, closest_deadline - now) - end - end - - requests.each do |req| - req.interrupt if req.expired?(now) - end - requests.reject!(&:done?) + now = 0.0 + QUEUE_MUTEX.synchronize do + while (now = Process.clock_gettime(Process::CLOCK_MONOTONIC)) < closest_deadline and QUEUE.empty? + CONDVAR.wait(QUEUE_MUTEX, closest_deadline - now) end end + + requests.each do |req| + req.interrupt if req.expired?(now) + end + requests.reject!(&:done?) + end + end + end + private_class_method :create_timeout_thread + + def self.ensure_timeout_thread_created + unless @timeout_thread and @timeout_thread.alive? + TIMEOUT_THREAD_MUTEX.synchronize do + unless @timeout_thread and @timeout_thread.alive? + @timeout_thread = create_timeout_thread + end end end end diff --git a/test/test_timeout.rb b/test/test_timeout.rb index 58eb8421db..76de38949d 100644 --- a/test/test_timeout.rb +++ b/test/test_timeout.rb @@ -139,4 +139,24 @@ class TestTimeout < Test::Unit::TestCase } assert(ok, bug11344) end + + def test_fork + omit 'fork not supported' unless Process.respond_to?(:fork) + r, w = IO.pipe + pid = fork do + r.close + begin + r = Timeout.timeout(0.01) { sleep 5 } + w.write r.inspect + rescue Timeout::Error + w.write 'timeout' + ensure + w.close + end + end + w.close + Process.wait pid + assert_equal 'timeout', r.read + r.close + end end