Skip to content

Commit 584443d

Browse files
bugfoodtvpartytonight
authored andcommitted
(PUP-11897) Handle EOF in order to avoid busy-loop
When EOF is reached, calls to IO.select() will return immediately, resulting in puppet executing the loop without any internal blocking. This causes up to 100% usage of a CPU core until the child finishes. If the child does its own stdout/stderr redirection before a long operation, for example, this can result in much wasted CPU usage. There is no need to execute the body of the loop after EOF is reached, so switch the Process.waitpid2 flags to block.
1 parent 3d4c34b commit 584443d

File tree

1 file changed

+7
-2
lines changed

1 file changed

+7
-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 &= ~Process::WNOHANG
237242
end
238243
end
239244

0 commit comments

Comments
 (0)