Skip to content

Commit ebfd084

Browse files
authored
Merge pull request #9106 from tvpartytonight/PUP-11897-7x
(PUP-11897) Fix excessive cpu usage after EOF from an exec
2 parents 3d4c34b + 4eb16e4 commit ebfd084

File tree

3 files changed

+21
-2
lines changed

3 files changed

+21
-2
lines changed

lib/puppet/util/execution.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,12 @@ def self.execute(command, options = NoOptionsSpecified)
222222
# Use non-blocking read to check for data. After each attempt,
223223
# check whether the child is done. This is done in case the child
224224
# forks and inherits stdout, as happens in `foo &`.
225-
226-
until results = Process.waitpid2(child_pid, Process::WNOHANG) #rubocop:disable Lint/AssignmentInCondition
225+
# If we encounter EOF, though, then switch to a blocking wait for
226+
# the child; after EOF, IO.select will never block and the loop
227+
# below will use maximum CPU available.
228+
229+
wait_flags = Process::WNOHANG
230+
until results = Process.waitpid2(child_pid, wait_flags) #rubocop:disable Lint/AssignmentInCondition
227231

228232
# If not done, wait for data to read with a timeout
229233
# This timeout is selected to keep activity low while waiting on
@@ -234,6 +238,7 @@ def self.execute(command, options = NoOptionsSpecified)
234238
output << reader.read_nonblock(4096) if ready
235239
rescue Errno::EAGAIN
236240
rescue EOFError
241+
wait_flags = 0
237242
end
238243
end
239244

spec/integration/type/exec_spec.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,19 @@
7575
end
7676
end
7777

78+
context 'when an exec sends an EOF' do
79+
let(:command) { ["/bin/bash", "-c", "exec /bin/sleep 1 >/dev/null 2>&1"] }
80+
81+
it 'should not take significant user time' do
82+
exec = described_class.new :command => command, :path => ENV['PATH']
83+
catalog.add_resource exec
84+
timed_apply = Benchmark.measure { catalog.apply }
85+
# In testing I found the user time before the patch in 4f35fd262e to be above
86+
# 0.3, after the patch it was consistently below 0.1 seconds.
87+
expect(timed_apply.utime).to be < 0.3
88+
end
89+
end
90+
7891
context 'when command is a string' do
7992
let(:command) { "ruby -e 'File.open(\"#{path}\", \"w\") { |f| f.print \"foo\" }'" }
8093

spec/unit/util/execution_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def stub_process_wait(exitstatus)
2929
allow(FFI::WIN32).to receive(:CloseHandle).with(thread_handle)
3030
else
3131
allow(Process).to receive(:waitpid2).with(pid, Process::WNOHANG).and_return(nil, [pid, double('child_status', :exitstatus => exitstatus)])
32+
allow(Process).to receive(:waitpid2).with(pid, 0).and_return(nil, [pid, double('child_status', :exitstatus => exitstatus)])
3233
allow(Process).to receive(:waitpid2).with(pid).and_return([pid, double('child_status', :exitstatus => exitstatus)])
3334
end
3435
end

0 commit comments

Comments
 (0)