jsoverson has asked for the wisdom of the Perl Monks concerning the following question:

In the following, extremely basic snippet of code, the idea is to fork off 10 processes all doing just the foo() subroutine and then sleep for 2 seconds.

But it only works if $SIG{CHLD} is not explicitly defined. Right now, even with $SIG{CHLD} defined as an empty anonymous subroutine sleep won't behave properly. If you comment out that first line, the script will behave as normally. Example output follows the script.

$SIG{CHLD} = sub { }; while (1) { for(1..10) {spawn(\&foo)} # fork off 10 &foo()'s my $s = sleep (2); # $s will hold the number of # seconds actually slept print "--------------------"; if ($s) { print "Slept for $s seconds\n"; }# If we slept at all else { print "Did not sleep at all\n"; }# If we didn't } sub spawn { return if fork(); # Fork and return the parent exit shift->(); # exec the coderef passed as arg } sub foo { print '*' for(1..5); }

Output with the $SIG{CHLD} line commented out (all output has some asterisks and dashes removed for display purposes):
**************************************---Slept for 2 seconds **************************************---Slept for 2 seconds **************************************---Slept for 2 seconds **************************************---Slept for 2 seconds **************************************---Slept for 2 seconds

And with the $SIG{CHLD} line active.
**************************************---Slept for 2 seconds **************************************---Did not sleep at all **************************************---Did not sleep at all *********************************---Did not sleep at all *******************************************---Slept for 2 seconds **********************************---Did not sleep at all *******************************************---Did not sleep at all

Does anyone have any idea what is going on? This is driving me nuts. Does anyone have an alternate sleep or pause routine that works in this situation?
This is obviously just example code that has been whittled down, but the idea behind it is that sleep just isn't working for me (on several machines) when I am dealing with forked children and want to handle them properly.

Replies are listed 'Best First'.
Re: Why is SIGCHLD affecting sleep?
by ikegami (Patriarch) on Aug 09, 2007 at 22:38 UTC

    From sleep,

    May be interrupted if the process receives a signal

    You could just go to sleep repeatedly until it's time.

    # Optional, but a good idea with short durations. use Time::HiRes qw( sleep time ); sub uninterrupted_sleep { my ($duration) = @_; die if !$duration; my $sleep_till = time() + $duration; while ($duration > 0) { sleep($duration); $duration = $sleep_till - time(); } }

    Update: Bug fix. Changed $duration -= to $duration =.

      Yeah, i just caught that from elsewhere. I must have read over that page a dozen times but every time my eyes removed the "a signal such as" part and just saw that it would be interrupted by SIGALRM.
Re: Why is SIGCHLD affecting sleep?
by jsoverson (Novice) on Aug 09, 2007 at 22:39 UTC
    And to answer myself (or post an answer i got from someone else, anyway):

    "Any signal you don't discard will wake up sleep"

    Which follows what I was seeing, it also explains why other code i've tried would work (or seem to, for the most part). Like:

    for(1..15) {sleep(1)}

    because every time an individual sleep would be interrupted it would just iterate through and try again.

    If there is another explanation, let me know, but this correlates with everything i've seen and definitely gives me a way to move forward.
      This is fine if you just need it to sleep for around 15 seconds. If you need it to sleep for "exactly" 15 seconds, you will probably want to use Time::HiRes and usleep, and do the elapsed time tracking yourself so you can be sure to get all the sleep you need.

      Remember, sleep does not sleep for the exact duration you tell it. It only sleeps for that many wall clock ticks and that could include a fraction of a whole second. Sleep for 1 fractional second 15 times and you could get much less sleep than you need.

      Ivan Heffner
      Sr. Software Engineer
      WhitePages.com, Inc.
Re: Why is SIGCHLD affecting sleep?
by bruceb3 (Pilgrim) on Aug 10, 2007 at 22:06 UTC
    There are a couple more options. You could just ignore the CHLD signal with -
    $SIG{CHLD} = 'IGNORE'
    And let the init process clean up the defunct child. Obviously if you need the exit status of the child or if you need to keep track of which children have finished, this won't work.
    Another option is to reap the children when you are ready.
    use strict; use POSIX ":sys_wait_h"; while (1) { for(1..10) {spawn(\&foo)} # fork off 10 &foo()'s my $s = sleep (2); # $s will hold the number of se +conds actually slept # clean up our children 1 while ((my $child = waitpid(-1, WNOHANG)) > 0); print "---"; if ($s) { print "Slept for $s seconds\n"; }# If we slept at all else { print "Did not sleep at all\n"; }# If we didn't } sub spawn { return if fork(); # Fork and return the parent exit shift->(); # exec the coderef passed as ar +g } sub foo { print "*" for (1..5); }