fix(child_process): fix inverted break condition in close method#5442
fix(child_process): fix inverted break condition in close method#5442magic-peach wants to merge 1 commit into
Conversation
In the close method's wait-for-exit loop, the condition 'break if living_process_exist' was inverted. It should be 'break unless living_process_exist'. The current code breaks out of the loop as soon as it finds living processes, skipping the wait for them to actually terminate. This allows child processes to become orphaned or zombie processes during shutdown, leaking resources held by those processes. The shutdown method at line 142 correctly implements this pattern: 'if process_exists: sleep; else: break'. Signed-off-by: Akanksha Trehun <akankshatrehun@gmail.com>
Watson1978
left a comment
There was a problem hiding this comment.
I looked into the history before reviewing, and I believe this condition is intentional rather than an inversion. break if living_process_exist was introduced in commit 39b9060 ("fix bug to never break loop", 2016). Before that change, close was a plain while pids.size > 0 loop that could fail to terminate; the living_process_exist tracking and break if living_process_exist were added specifically to guarantee the loop always exits.
The key difference from shutdown is the loop bound. shutdown's wait loop is bounded by exit_wait_timeout:
while Fluent::Clock.now < exit_wait_timeoutso "keep looping while a process is alive" is safe there. close has no such bound. Changing it to break unless living_process_exist turns close into an unbounded "loop while any process is alive," which reintroduces exactly the non-termination bug that 39b9060 fixed: if a child can't be reaped, shutdown would hang indefinitely.
Could you share a reproduction of the orphan/zombie behavior? With that we can work out the right bounded fix. As it stands I don't think we can flip this condition, since it would re-open the 2016 hang.
Summary
In the
closemethod's wait-for-exit loop (lib/fluent/plugin_helper/child_process.rb:194), the conditionbreak if living_process_existis inverted and should bebreak unless living_process_exist.The bug
The loop is designed to wait for all child processes to exit (or force-kill them after the kill timeout). The logic:
living_process_exist = trueand force-kill it if past the timeoutbreak if living_process_exist— this is backwardsThe current code breaks out of the loop as soon as it finds any living process, immediately proceeding to
superand continuing shutdown without waiting for those processes to actually terminate. This allows child processes to become orphaned or zombie processes.Comparison with
shutdownmethodThe
shutdownmethod (line 142) correctly implements the same wait-for-exit pattern:The
closemethod should follow the same pattern: keep looping while processes are alive, break when they're all gone.