in reply to CHLD handler stopped working
G'day halecommarachel,
"I had a simpler version of the below code working, but when I added some complexity, the signal handler for CHLD stopped reaping zombies. Here is my code now:"
Actually showing us what was working and what you added to stop it working would have been very useful. As it stands, we have no way of telling what change caused the failure.
Furthermore, cutting your code down to the absolute minimum which still reproduced your problem may have helped you to solve the problem by yourself. Even if you still couldn't solve it, we wouldn't have to wade through totally irrelevant code looking for a problem.
Your immediate problem is the position of the exit statement. You probably intended to exit only from the child process.
Here's a cut-down version of your script which (roughly) reproduces the problem you described:
#!/usr/bin/env perl -l use strict; use warnings; use autodie qw{:all}; use POSIX qw{WNOHANG}; my %children; $SIG{CHLD} = sub { local ($!, $?); my $pid = waitpid(-1, WNOHANG); return if $pid == -1; return unless defined $children{$pid}; delete $children{$pid}; print "SIG{CHLD}: PID $pid ", kill(0 => $pid) ? 'NOT ' : '', 'reap +ed.'; }; print "PARENT($$): Before multi_dir() call"; multi_dir(); sub multi_dir { for (0 .. 1) { my $pid = fork; die 'Undefined PID from fork()' unless defined $pid; if ($pid) { # parent $children{$pid} = 1; print "PARENT($$): Started child process with PID: $pid"; print "PARENT($$): ", scalar keys %children, ' child proce +sses'; } else { # child print "CHILD($$): New process started"; system 'echo PPID=$PPID PID=$$; sleep 1'; print "CHILD($$): system() finished"; } exit; # <<<=== PROBLEM HERE! } }
By moving the exit to the child code, the script no longer hangs:
sub multi_dir { ... print "CHILD($$): system() finished"; exit; # <<<=== PROBLEM FIXED! } } }
Having said that, I recommend you read what exit says about exiting from subroutines. Even if the simple fix I described above works for you now, when you next add a little more "complexiy" you may find yourself with problems again. Perhaps you could return with the child PID instead of using exit; then, if the end of &multi_dir is reached, return with zero — that way, you can test the return value of multi_dir() and exit if it's the child or continue with the main script if it's the parent. Here's an example:
... print "PARENT($$): Before multi_dir() call"; my $multi_dir_return = multi_dir(); if ($multi_dir_return) { print "CHILD($multi_dir_return): exiting"; exit; } print "PARENT($$): After multi_dir() call"; sub multi_dir { ... print "CHILD($$): system() finished"; return $$; } } return 0; }
Your code has some other (potential) problems. Here's a couple I spotted:
This:
if ($limit) { @subs = @subs[1 .. $limit]; }
would probably have been better as something closer to:
if (defined $limit and $limit > @subs) { @subs = @subs[0 .. $limit - 1]; }
And this:
while (keys %children >= $max) { }
is potentially an infinite loop. It's also repeatedly checking the condition, and doing nothing else, which is chewing up a lot CPU cycles unnecessarily. Consider adding a sleep with an alarm (see Time::HiRes for times with finer granularity than whole seconds).
-- Ken
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re^2: CHLD handler stopped working
by halecommarachel (Sexton) on May 12, 2014 at 22:11 UTC | |
by kcott (Archbishop) on May 13, 2014 at 02:31 UTC | |
by halecommarachel (Sexton) on May 21, 2014 at 17:54 UTC | |
by kcott (Archbishop) on May 27, 2014 at 12:47 UTC |