Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make waitpid block the loop

The following code runs 2 children, who will wait for 10 seconds and terminate. The parent is sitting in a loop, waiting for the children to terminate:

#!/usr/bin/perl

use strict;
use warnings;
use POSIX ":sys_wait_h";

sub func
# {{{
{
      my $i = shift;
      print "$i started\n";
      $| = 1;
      sleep(10);
      print "$i finished\n";
}
# }}}

my $n = 2;
my @children_pids;

for (my $i = 0; $i < $n; $i++) {
      if ((my $pid = fork()) == 0) {
            func($i);
            exit(0);
      } else {
            $children_pids[$i] = $pid;
      }
}

my $stillWaiting;
do {
      $stillWaiting = 0;
      for (my $i = 0; $i < $n; ++$i) {
            if ($children_pids[$i] > 0)
            {
                  if (waitpid($children_pids[$i], WNOHANG) != 0) {
                        # Child is done
                        print "child done\n";
                        $children_pids[$i] = 0;
                  } else {
                        # Still waiting on this child
                        #print "waiting\n";
                        $stillWaiting = 1;
                  }
            }
            #Give up timeslice and prevent hard loop: this may not work on all flavors of Unix
            sleep(0);
      }
} while ($stillWaiting);

print "parent finished\n";

The code is based upon this answer: Multiple fork() Concurrency

It works correctly, but the parent loop is eating processor time. top command gives this:

enter image description here

Here the answer says:

As an additional bonus, the loop will block on waitpid while children are running, so you don't need a busy loop while you wait.

But for me it doesn't block. What's wrong?

like image 276
user4035 Avatar asked Jun 07 '13 09:06

user4035


1 Answers

You're passing the WNOHANG flag, which makes the call non-blocking. Remove this flag and waitpid will wait at 0% CPU until the child quits.

If you take this approach, you could simplify the code. There's no need to loop until a child is finished, because the blocking waitpid call will do that for you:

for (my $i = 0; $i < $n; ++$i) {
    if ($children_pids[$i] > 0) {
          waitpid($children_pids[$i], 0);
          print "child done\n";
          $children_pids[$i] = 0;
    }
}

Alternatively, change the sleep call to wait for one second. Then your program will check for finished children every second, without pushing up CPU usage.

like image 153
Andomar Avatar answered Nov 13 '22 14:11

Andomar