Wait with a negative pid to wait for any child

This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the unix category.

Last Updated: 2022-05-26

Say you had a program that spawned some child processes to do some work.

pids = []

work_items.each do |work_item|
  command = handle(work_item)
  # Create a child process
  pid = Process.spawn(command)
  pids << pid
end

At some point you wait for this work to be finished:

pids.each do |pid|
  Process.waitpid(pid)
  puts "Finished #{pid}"
end

What's the confusing part here? Well imagine the 1st process was the slowest (10s) and the 4th in the pids array the fastest (1s). That information gets lost because we stop on the first entry in the array and wait 10s. As soon as that's finished, you'll see the others seemingly "finish" instantaneously, obscuring the fact that the 4th finished much sooner (and blocked progress)

One solution is to wait for any child process (i.e. the next one that finishes)

begin
  # give -1 as an argument waits for _any_ PID
  Process.waitpid(-1)
end until pids.empty? 

Resources