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

Monks-

I feel that I should be able to figure this one out, but am hitting a brick wall.

Why does the following give me rc=-1, and if I comment out $SIG{CHLD} then rc=0?

Thanks

-Craig

update: I believe I'm asking the same question that I asked a while ago (although I was using backticks then). Results do seem to vary based on platform. Should this be reported as a perl bug, or do I just need to parse out the reason for the -1 return from $!, as the docs state (thanks Bloodnok++)

use English; use strict; use warnings; use Data::Dumper; # Comment this out and rc=0... $SIG{CHLD} = sub { _lostChild(wait()) }; sub _lostChild { print "_lostChild args:\n", Dumper(\@_), "\n" }; my $rc=system('date'); print STDERR "rc=$rc\n";

Replies are listed 'Best First'.
Re: system() returns -1 with $SIG{CHLD}?
by almut (Canon) on Feb 11, 2009 at 19:53 UTC

    I do get

    Wed Feb 11 20:52:08 CET 2009 _lostChild args: $VAR1 = [ -1 ]; rc=0

    Probably a matter of who calls wait() first... (i.e. system() or you in your signal handler).  Remember that signals are delivered asynchronously.

    Update: an strace in my case shows:

    ... wait4(28813, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 28813 --- SIGCHLD (Child exited) @ 0 (0) --- rt_sigreturn(0) = 28813 rt_sigaction(SIGINT, {SIG_DFL}, NULL, 8) = 0 rt_sigaction(SIGQUIT, {SIG_DFL}, NULL, 8) = 0 read(6, "", 4) = 0 close(6) = 0 rt_sigprocmask(SIG_BLOCK, [CHLD], NULL, 8) = 0 wait4(-1, 0x7fff3bda0c04, 0, NULL) = -1 ECHILD (No child processe +s) write(1, "_lostChild args:\n", 17) = 17 write(1, "$VAR1 = [\n -1\n ]"..., 34) = 34 write(1, "\n", 1) = 1 rt_sigprocmask(SIG_UNBLOCK, [CHLD], NULL, 8) = 0 write(2, "rc=0\n", 5) = 5 close(4) = 0 exit_group(0) = ?

    The first wait is from Perl's system(). Then the SIGCHLD is received, which leads to the next wait call from within the signal handler, which gets ECHILD, as the process has already been reaped by the previous system()'s wait...

    In your case, the SIGCHLD presumably arrives a tad earlier (before the first wait).

Re: system() returns -1 with $SIG{CHLD}?
by salva (Canon) on Feb 11, 2009 at 20:23 UTC
    under the hood, system should be something similar to:
    sub system { my $pid = fork; if (!$pid) { exec @_; exit(-1); } while (1) { if (waitpid($pid, \$?, 0) > 0) { # supposing this calls the C wait +pid! return $? } if ($! == EINTR) { run_signal_handlers(); next; } return -1; } }
    Setting a signal handler for SIG{CHLD} probably interrupts the waitpid call, your handler is run and as it calls wait(), the process just run gets reaped.

    When waitpid is called again, it finds that the child it should be waiting for has been already reaped, and so, returns an error.

    If you check $! it should contain the value ECHILD.

    update: I think this could be considered a perl bug, so report it to p5p!

Re: system() returns -1 with $SIG{CHLD}?
by Bloodnok (Vicar) on Feb 11, 2009 at 19:29 UTC
    Are you forgetting that system returns a combination of the return status code together with the signal number with which the code was run ?

    Probably worth investigating $! - as suggested in ...Return value of -1 indicates a failure to start the program or an error of the wait(2) system call (inspect $! for the reason)...

    A user level that continues to overstate my experience :-))

      When running the code I get -1 from wait and 0 from system. $! tells me No child processes. That makes sense since both system also does a wait.

      Why are you using $SIG{CHLD} with system? You shouldn't wait for a child launched using system.

        Why are you using $SIG{CHLD} with system? You shouldn't wait for a child launched using system.

        This was just a sample piece of code distilled down from a much larger perl program. In the big program, I'm running commands in the background, that take an arbitrary amount of time to complete. I don't want to be leaving any zombie processes around, so I used wait() with $SIG{CHLD} to mitigate this.

        Should I be doing something differently here, or should I be responsible for managing the handling of $SIG{CHLD} whenever I want to run a system() command?

        update: The zombies are left after forking a background process, not using system. I didn't make that clear

Re: system() returns -1 with $SIG{CHLD}?
by salva (Canon) on Feb 12, 2009 at 08:48 UTC
    reply to your update:

    My advice is to report the problem and let the porters decide if it is a bug or not. system already protects itself against other signals, and probably the infrastructure to filter some pids from wait already exists in order to handle asynchronous processes created by open FOO, '|-', ....

    In the meantime...

    sub mysystem { my $signal_received = 0; my $old_signal_handler = $SIG{CHLD}; local $SIG{CHLD} = sub { $signal_received = 1}; system @_; $old_signal_handler->() if $signal_received; }
    And inside your signal handler ensure that $! and $? are localized, so that the values of this globals don't get unexpectedly changed at any place in your program.
Re: system() returns -1 with $SIG{CHLD}?
by targetsmart (Curate) on Feb 12, 2009 at 08:10 UTC
    I too faced and solved this problem here http://www.perlmonks.org/?node_id=735947

    Vivek
    -- In accordance with the prarabdha of each, the One whose function it is to ordain makes each to act. What will not happen will never happen, whatever effort one may put forth. And what will happen will not fail to happen, however much one may seek to prevent it. This is certain. The part of wisdom therefore is to stay quiet.