[前][次][番号順一覧][スレッド一覧]

ruby-changes:68511

From: Nobuyoshi <ko1@a...>
Date: Sun, 17 Oct 2021 16:34:24 +0900 (JST)
Subject: [ruby-changes:68511] 478187e9a3 (master): Timeout parallel test worker processes

https://git.ruby-lang.org/ruby.git/commit/?id=478187e9a3

From 478187e9a33b7af5b11e570f5133c963af6e1165 Mon Sep 17 00:00:00 2001
From: Nobuyoshi Nakada <nobu@r...>
Date: Tue, 5 Oct 2021 09:09:15 +0900
Subject: Timeout parallel test worker processes

---
 tool/lib/test/unit.rb | 32 +++++++++++++++++++++++++++-----
 1 file changed, 27 insertions(+), 5 deletions(-)

diff --git a/tool/lib/test/unit.rb b/tool/lib/test/unit.rb
index 3067809153..32694bc27d 100644
--- a/tool/lib/test/unit.rb
+++ b/tool/lib/test/unit.rb
@@ -281,6 +281,7 @@ module Test https://github.com/ruby/ruby/blob/trunk/tool/lib/test/unit.rb#L281
             options[:parallel] ||= 1
           end
         end
+        @worker_timeout = EnvUtil.apply_timeout_scale(options[:worker_timeout] || 180)
         super
       end
 
@@ -303,6 +304,10 @@ module Test https://github.com/ruby/ruby/blob/trunk/tool/lib/test/unit.rb#L304
           options[:parallel] = a.to_i
         end
 
+        opts.on '--worker-timeout=N', Integer, "Timeout workers not responding in N seconds" do |a|
+          options[:worker_timeout] = a
+        end
+
         opts.on '--separate', "Restart job process after one testcase has done" do
           options[:parallel] ||= 1
           options[:separate] = true
@@ -337,6 +342,7 @@ module Test https://github.com/ruby/ruby/blob/trunk/tool/lib/test/unit.rb#L342
 
         attr_reader :quit_called
         attr_accessor :start_time
+        attr_accessor :response_at
 
         @@worker_number = 0
 
@@ -350,6 +356,7 @@ module Test https://github.com/ruby/ruby/blob/trunk/tool/lib/test/unit.rb#L356
           @loadpath = []
           @hooks = {}
           @quit_called = false
+          @response_at = nil
         end
 
         def name
@@ -369,6 +376,7 @@ module Test https://github.com/ruby/ruby/blob/trunk/tool/lib/test/unit.rb#L376
             puts "run #{task} #{type}"
             @status = :prepare
             @start_time = Time.now
+            @response_at = @start_time
           rescue Errno::EPIPE
             died
           rescue IOError
@@ -385,6 +393,7 @@ module Test https://github.com/ruby/ruby/blob/trunk/tool/lib/test/unit.rb#L393
 
         def read
           res = (@status == :quit) ? @io.read : @io.gets
+          @response_at = Time.now
           res && res.chomp
         end
 
@@ -515,9 +524,11 @@ module Test https://github.com/ruby/ruby/blob/trunk/tool/lib/test/unit.rb#L524
         @ios.delete worker.io
       end
 
-      def quit_workers
+      def quit_workers(&cond)
         return if @workers.empty?
+        closed = []
         @workers.reject! do |worker|
+          next unless cond&.call(worker)
           begin
             Timeout.timeout(1) do
               worker.quit
@@ -525,9 +536,11 @@ module Test https://github.com/ruby/ruby/blob/trunk/tool/lib/test/unit.rb#L536
           rescue Errno::EPIPE
           rescue Timeout::Error
           end
+          closed << worker
           worker.close
         end
 
+        return closed if cond
         return if @workers.empty?
         begin
           Timeout.timeout(0.2 * @workers.size) do
@@ -646,14 +659,23 @@ module Test https://github.com/ruby/ruby/blob/trunk/tool/lib/test/unit.rb#L659
         begin
           [@tasks.size, @options[:parallel]].min.times {launch_worker}
 
-          while _io = IO.select(@ios)[0]
-            break if _io.any? do |io|
+          while true
+            timeout = [(@workers.filter_map {|w| w.response_at}.min&.-(Time.now) || 0) + @worker_timeout, 1].max
+
+            if !(_io = IO.select(@ios, nil, nil, timeout))
+              timeout = Time.now - @worker_timeout
+              @tasks.unshift(*quit_workers {|w| w.response_at < timeout}&.map(&:real_file))
+            elsif _io.first.any? {|io|
               @need_quit or
                 (deal(io, type, result, rep).nil? and
                  !@workers.any? {|x| [:running, :prepare].include? x.status})
+            }
+              break
             end
-            if @jobserver and @job_tokens and !@tasks.empty? and !@workers.any? {|x| x.status == :ready}
-              t = @jobserver[0].read_nonblock([@tasks.size, @options[:parallel]].min, exception: false)
+            if @jobserver and @job_tokens and !@tasks.empty? and
+               ((newjobs = [@tasks.size, @options[:parallel]].min) > @workers.size or
+                !@workers.any? {|x| x.status == :ready})
+              t = @jobserver[0].read_nonblock(newjobs, exception: false)
               if String === t
                 @job_tokens << t
                 t.size.times {launch_worker}
-- 
cgit v1.2.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]