Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.

Commit d9e7d3d

Browse files
authored
Merge pull request #2739 from rspec/detach-process-to-avoid-zombie
Detach bisect subprocesses to avoid making zombie processes
2 parents 1dbf4a4 + 07754b6 commit d9e7d3d

File tree

3 files changed

+52
-3
lines changed

3 files changed

+52
-3
lines changed

Changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ Bug Fixes:
1414
* Ensure custom error codes are returned from bisect runs. (Jon Rowe, #2732)
1515
* Ensure `RSpec::Core::Configuration` predicate config methods return booleans.
1616
(Marc-André Lafortune, #2736)
17+
* Prevent `rspec --bisect` from generating zombie processes while executing
18+
bisect runs. (Benoit Tigeot, Jon Rowe, #2739)
1719

1820
### 3.9.2 / 2020-05-02
1921
[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.9.1...v3.9.2)

lib/rspec/core/bisect/fork_runner.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,12 @@ def initialize(runner, channel)
9191
end
9292

9393
def dispatch_specs(run_descriptor)
94-
fork { run_specs(run_descriptor) }
94+
pid = fork { run_specs(run_descriptor) }
9595
# We don't use Process.waitpid here as it was causing bisects to
96-
# block due to the file descriptor limit on OSX / Linux.
96+
# block due to the file descriptor limit on OSX / Linux. We need
97+
# to detach the process to avoid having zombie processes
98+
# consuming slots in the kernel process table during bisect runs.
99+
Process.detach(pid)
97100
end
98101

99102
private

spec/integration/bisect_spec.rb

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,58 @@ def bisect(cli_args, expected_status=nil)
3333
end
3434
end
3535

36-
context "when the bisect commasaturingnd is long" do
36+
context "when the bisect command saturates the pipe" do
3737
# On OSX and Linux a file descriptor limit meant that the bisect process got stuck at a certain limit.
3838
# This test demonstrates that we can run large bisects above this limit (found to be at time of commit).
3939
# See: https://github.com/rspec/rspec-core/pull/2669
4040
it 'does not hit pipe size limit and does not get stuck' do
4141
output = bisect(%W[spec/rspec/core/resources/blocking_pipe_bisect_spec.rb_], 1)
4242
expect(output).to include("No failures found.")
4343
end
44+
45+
it 'does not leave zombie processes', :unless => RSpec::Support::OS.windows? do
46+
bisect(['--format', 'json', 'spec/rspec/core/resources/blocking_pipe_bisect_spec.rb_'], 1)
47+
48+
zombie_process = RSpecChildProcess.new(Process.pid).zombie_process
49+
expect(zombie_process).to eq([]), <<-MSG
50+
Expected no zombie processes got #{zombie_process.count}:
51+
#{zombie_process}
52+
MSG
53+
end
54+
end
55+
56+
class RSpecChildProcess
57+
Ps = Struct.new(:pid, :ppid, :state, :command)
58+
59+
def initialize(pid)
60+
@list = child_process_list(pid)
61+
end
62+
63+
def zombie_process
64+
@list.select { |child_process| child_process.state =~ /Z/ }
65+
end
66+
67+
private
68+
69+
def child_process_list(pid)
70+
childs_process_list = []
71+
ps_pipe = `ps -o pid=,ppid=,state=,args= | grep #{pid}`
72+
73+
ps_pipe.split(/\n/).map do |line|
74+
ps_part = line.lstrip.split(/\s+/)
75+
76+
next unless ps_part[1].to_i == pid
77+
78+
child_process = Ps.new
79+
child_process.pid = ps_part[0]
80+
child_process.ppid = ps_part[1]
81+
child_process.state = ps_part[2]
82+
child_process.command = ps_part[3..-1].join(' ')
83+
84+
childs_process_list << child_process
85+
end
86+
childs_process_list
87+
end
4488
end
4589
end
4690
end

0 commit comments

Comments
 (0)