1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Timeout parallel test worker processes

This commit is contained in:
Nobuyoshi Nakada 2021-10-05 09:09:15 +09:00
parent 1831693c1f
commit 478187e9a3
Notes: git 2021-10-17 16:34:20 +09:00

View file

@ -281,6 +281,7 @@ module Test
options[:parallel] ||= 1 options[:parallel] ||= 1
end end
end end
@worker_timeout = EnvUtil.apply_timeout_scale(options[:worker_timeout] || 180)
super super
end end
@ -303,6 +304,10 @@ module Test
options[:parallel] = a.to_i options[:parallel] = a.to_i
end 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 opts.on '--separate', "Restart job process after one testcase has done" do
options[:parallel] ||= 1 options[:parallel] ||= 1
options[:separate] = true options[:separate] = true
@ -337,6 +342,7 @@ module Test
attr_reader :quit_called attr_reader :quit_called
attr_accessor :start_time attr_accessor :start_time
attr_accessor :response_at
@@worker_number = 0 @@worker_number = 0
@ -350,6 +356,7 @@ module Test
@loadpath = [] @loadpath = []
@hooks = {} @hooks = {}
@quit_called = false @quit_called = false
@response_at = nil
end end
def name def name
@ -369,6 +376,7 @@ module Test
puts "run #{task} #{type}" puts "run #{task} #{type}"
@status = :prepare @status = :prepare
@start_time = Time.now @start_time = Time.now
@response_at = @start_time
rescue Errno::EPIPE rescue Errno::EPIPE
died died
rescue IOError rescue IOError
@ -385,6 +393,7 @@ module Test
def read def read
res = (@status == :quit) ? @io.read : @io.gets res = (@status == :quit) ? @io.read : @io.gets
@response_at = Time.now
res && res.chomp res && res.chomp
end end
@ -515,9 +524,11 @@ module Test
@ios.delete worker.io @ios.delete worker.io
end end
def quit_workers def quit_workers(&cond)
return if @workers.empty? return if @workers.empty?
closed = []
@workers.reject! do |worker| @workers.reject! do |worker|
next unless cond&.call(worker)
begin begin
Timeout.timeout(1) do Timeout.timeout(1) do
worker.quit worker.quit
@ -525,9 +536,11 @@ module Test
rescue Errno::EPIPE rescue Errno::EPIPE
rescue Timeout::Error rescue Timeout::Error
end end
closed << worker
worker.close worker.close
end end
return closed if cond
return if @workers.empty? return if @workers.empty?
begin begin
Timeout.timeout(0.2 * @workers.size) do Timeout.timeout(0.2 * @workers.size) do
@ -646,14 +659,23 @@ module Test
begin begin
[@tasks.size, @options[:parallel]].min.times {launch_worker} [@tasks.size, @options[:parallel]].min.times {launch_worker}
while _io = IO.select(@ios)[0] while true
break if _io.any? do |io| 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 @need_quit or
(deal(io, type, result, rep).nil? and (deal(io, type, result, rep).nil? and
!@workers.any? {|x| [:running, :prepare].include? x.status}) !@workers.any? {|x| [:running, :prepare].include? x.status})
}
break
end end
if @jobserver and @job_tokens and !@tasks.empty? and !@workers.any? {|x| x.status == :ready} if @jobserver and @job_tokens and !@tasks.empty? and
t = @jobserver[0].read_nonblock([@tasks.size, @options[:parallel]].min, exception: false) ((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 if String === t
@job_tokens << t @job_tokens << t
t.size.times {launch_worker} t.size.times {launch_worker}