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/