Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   Perl Misc (http://www.velocityreviews.com/forums/f67-perl-misc.html)
-   -   signal handler is not called while piped open is active (http://www.velocityreviews.com/forums/t914409-signal-handler-is-not-called-while-piped-open-is-active.html)

Glenn 12-29-2010 01:48 AM

signal handler is not called while piped open is active
 
Given the following simple script:

#!/usr/bin/perl -w --

my $terminate = 0;
my $failed = 0;

sub print_signal {
my $signame = shift;
$terminate = 1;
die "got a SIG$signame signal\n";
}

$SIG{INT} = \&print_signal;
$SIG{QUIT} = \&print_signal;
$SIG{TERM} = \&print_signal;

open PIPE, '|-', "sleep 30";
print PIPE "nighty night\n";
$failed |= ! close PIPE;
print "OS error: $!\n";
print "Child error: $?\n";
if (!$terminate) {
print "sleeping after PIPE handling ...\n";
sleep 30;
}
print "terminated by signal\n" if $terminate;
print "failed: $failed\n";

Why is the signal hander not called if I interrupt the script with
SIGINT or SIGQUIT while the PIPE is open? I don't see this behavior
documented anywhere. I expect the %SIG settings to be obeyed as
documented, both during and after the PIPE processing.

Here's the complete behavior, in detail, when the signal is sent while
the PIPE i/o is underway. In each case, I run the script in a shell,
in the foreground.

SIGINT from keyboard => kills PIPE i/o but does not call signal
handler
SIGQUIT from keyboard => kills PIPE i/o but does not call signal
handler
SIGINT from another terminal => has no effect whatsoever
SIGQUIT from another terminal => has no effect whatsoever
SIGTERM from another terminal => signal handler is called

Why is there any difference at all depending on where the signal comes
from, and on what signal is sent?

How do we get these blatant failures documented in the Perl manual?

Note that the signal handlers are clearly still in play after the PIPE
i/o is killed, because a signal sent from any source during the
script's own later sleep() call (either SIGINT, SIGQUIT, or SIGTERM)
does kill the script.

Under this circumstance, how do I get a reliable indication that a
signal was sent (and not just that the PIPE handling failed for quite
possibly some other reason)?

I'm using Perl 5.8.8 for my testing.

C.DeRykus 12-29-2010 10:09 AM

Re: signal handler is not called while piped open is active
 
On Dec 28, 5:48*pm, Glenn <eponymousal...@yahoo.com> wrote:
> Given the following simple script:
>
> #!/usr/bin/perl -w --
>
> my $terminate = 0;
> my $failed * *= 0;
>
> sub print_signal {
> * * my $signame = shift;
> * * $terminate = 1;
> * * die "got a SIG$signame signal\n";
>
> }
>
> $SIG{INT} *= \&print_signal;
> $SIG{QUIT} = \&print_signal;
> $SIG{TERM} = \&print_signal;
>
> open PIPE, '|-', "sleep 30";
> print PIPE "nighty night\n";
> $failed |= ! close PIPE;
> print "OS error: *$!\n";
> print "Child error: *$?\n";
> if (!$terminate) {
> * * print "sleeping after PIPE handling ...\n";
> * * sleep 30;}
>
> print "terminated by signal\n" if $terminate;
> print "failed: *$failed\n";
>
> Why is the signal hander not called if I interrupt the script with
> SIGINT or SIGQUIT while the PIPE is open? *I don't see this behavior
> documented anywhere. *I expect the %SIG settings to be obeyed as
> documented, both during and after the PIPE processing.
>
> Here's the complete behavior, in detail, when the signal is sent while
> the PIPE i/o is underway. *In each case, I run the script in a shell,
> in the foreground.
>
> SIGINT from keyboard => kills PIPE i/o but does not call signal
> handler
> SIGQUIT from keyboard => kills PIPE i/o but does not call signal
> handler
> SIGINT from another terminal => has no effect whatsoever
> SIGQUIT from another terminal => has no effect whatsoever
> SIGTERM from another terminal => signal handler is called
>
> Why is there any difference at all depending on where the signal comes
> from, and on what signal is sent?
>
> How do we get these blatant failures documented in the Perl manual?
>
> Note that the signal handlers are clearly still in play after the PIPE
> i/o is killed, because a signal sent from any source during the
> script's own later sleep() call (either SIGINT, SIGQUIT, or SIGTERM)
> does kill the script.


Yes, I see evidence of the INT handler if,
for instance, I add:

END { sleep 5; } # --> got a SIGINT signal

I'm not sure why only the signal handler output is
seen only in the parent process however.

>
> Under this circumstance, how do I get a reliable indication that a
> signal was sent (and not just that the PIPE handling failed for quite
> possibly some other reason)?
>
> I'm using Perl 5.8.8 for my testing.


That should reliably be returned by your
check of $! and $? after close().

On 5.10.1 and FreeBSD, your child $? check was:

Child error: 2

which is waitpid output indicating that child
termination was due to a SIGINT.
(${^CHILD_ERROR_NATIVE} is also 2.)
See perldoc -f waitpid and perldoc -f wait.

You can inspect the child status to see if there
was signal termination:

$signal_termination = $? & 127
See perldoc -f system

--
Charles DeRykus





Glenn 12-29-2010 05:37 PM

Re: signal handler is not called while piped open is active
 
> Yes, I see evidence of the INT handler if,
> for instance, I add:
>
> * * END { sleep 5; } *# --> got a SIGINT signal
>
> I'm not sure why only the signal handler output is
> seen only in the parent process however.


The child process is running /bin/sleep, not Perl.

> > Under this circumstance, how do I get a reliable indication that a
> > signal was sent (and not just that the PIPE handling failed for quite
> > possibly some other reason)?

>
> > I'm using Perl 5.8.8 for my testing.

>
> That should reliably be returned by your
> check of $! and $? after close().
>
> On 5.10.1 and FreeBSD, your child $? check was:
>
> * *Child error: 2
>
> which is waitpid output indicating that child
> termination *was due to a SIGINT.
> (${^CHILD_ERROR_NATIVE} is also 2.)
> See perldoc -f waitpid and perldoc -f wait.
>
> You can inspect the child status to see if there
> was signal termination:
>
> * *$signal_termination = $? & 127
> See perldoc -f system


I was afraid the test code would elicit that kind of response. The
point is, I'm not really interested in the child status -- that's just
a red herring, something I'm reporting in this test program only
because it's something I can probe. What I care about is whether or
not the parent process received a signal. In this testing, the child
happens to receive the same signal if I generate it from the keyboard,
simply because (I'm guessing) it's sent to the entire process tree (by
the shell). But if I send the signal from a different terminal,
targeted specifically at the parent process, then the child process
won't be receiving it, and thus its exit status is irrelevant.

C.DeRykus 12-29-2010 07:55 PM

Re: signal handler is not called while piped open is active
 
On Dec 29, 9:37*am, Glenn <eponymousal...@yahoo.com> wrote:
> > Yes, I see evidence of the INT handler if,
> > for instance, I add:

>
> > * * END { sleep 5; } *# --> got a SIGINT signal

>
> > I'm not sure why only the signal handler output is
> > seen only in the parent process however.

>
> The child process is running /bin/sleep, not Perl.
>


Oh d'oh... exec happens immediately.

>
> > > Under this circumstance, how do I get a reliable indication that a
> > > signal was sent (and not just that the PIPE handling failed for quite
> > > possibly some other reason)?

>
> > > I'm using Perl 5.8.8 for my testing.

>
> > That should reliably be returned by your
> > check of $! and $? after close().

>
> > On 5.10.1 and FreeBSD, your child $? check was:

>
> > * *Child error: 2

>
> > which is waitpid output indicating that child
> > termination *was due to a SIGINT.
> > (${^CHILD_ERROR_NATIVE} is also 2.)
> > See perldoc -f waitpid and perldoc -f wait.

>
> > You can inspect the child status to see if there
> > was signal termination:

>
> > * *$signal_termination = $? & 127
> > See perldoc -f system

>
> I was afraid the test code would elicit that kind of response. *The
> point is, I'm not really interested in the child status -- that's just
> a red herring, something I'm reporting in this test program only
> because it's something I can probe. *What I care about is whether or
> not the parent process received a signal. *In this testing, the child
> happens to receive the same signal if I generate it from the keyboard,
> simply because (I'm guessing) it's sent to the entire process tree (by
> the shell). *But if I send the signal from a different terminal,
> targeted specifically at the parent process, then the child process
> won't be receiving it, and thus its exit status is irrelevant.


Hm, the program does ignore signals
(except KILL and STOP of course) sent to
just the parent from another terminal.

However, and I'm not sure why, if I signal
the process group - not just the parent,
it works:

kill -2 -11730 # from another terminal

The result in the running program:

parent=11730; # added debug print

got a SIGINT signal

--
Charles DeRykus

Skye Shaw!@#$ 01-03-2011 07:08 AM

Re: signal handler is not called while piped open is active
 
On Dec 28 2010, 5:48*pm, Glenn <eponymousal...@yahoo.com> wrote:
>
> <snip code>
>
> $SIG{INT} *= \&print_signal;
> $SIG{QUIT} = \&print_signal;
> $SIG{TERM} = \&print_signal;
>
> open PIPE, '|-', "sleep 30";
> print PIPE "nighty night\n";
> $failed |= ! close PIPE;
>
> <snip code>



> Why is the signal hander not called if I interrupt the script with
> SIGINT or SIGQUIT while the PIPE is open? *I don't see this behavior
> documented anywhere. *
> I expect the %SIG settings to be obeyed as
> documented


What docs are you referring to?

Modern perls can defer the delivery of signals to the %SIG handlers.
Here it appears that when close() is blocking on a handle attached to
a child process perl never delivers the INT and QUIT signals.

In similar cases these signals are delivered: a fork()/wait()
combination, blocking for IO, etc...

Maybe this behavior underscores the nice convenience Perl provides via
a piped open()? Not only do you not have to pipe(), dup(), fork(),
exec() and wait(), but you can almost always reap your child without
having to get caught up in the signal world.

> Here's the complete behavior, in detail, when the signal is sent while
> the PIPE i/o is underway. *In each case, I run the script in a shell,
> in the foreground.
>
> SIGINT from keyboard => kills PIPE i/o but does not call signal
> handler
> SIGQUIT from keyboard => kills PIPE i/o but does not call signal
> handler
> SIGINT from another terminal => has no effect whatsoever
> SIGQUIT from another terminal => has no effect whatsoever
> SIGTERM from another terminal => signal handler is called


Given the close() issue above this sounds "right".

The parent ignores all but SIGTERM and, in the first two cases, the
signal was sent to the process group so the child receives it and the
parent's close() returns.

> Why is there any difference at all depending on where the signal comes
> from, and on what signal is sent?


Signals are odd beasts.

C.DeRykus 01-03-2011 01:47 PM

Re: signal handler is not called while piped open is active
 
On Jan 2, 11:08*pm, "Skye Shaw!@#$" <skye.s...@gmail.com> wrote:
> On Dec 28 2010, 5:48*pm, Glenn <eponymousal...@yahoo.com> wrote:
>
>
>
> > <snip code>

>
> > $SIG{INT} *= \&print_signal;
> > $SIG{QUIT} = \&print_signal;
> > $SIG{TERM} = \&print_signal;

>
> > open PIPE, '|-', "sleep 30";
> > print PIPE "nighty night\n";
> > $failed |= ! close PIPE;

>
> > <snip code>
> > Why is the signal hander not called if I interrupt the script with
> > SIGINT or SIGQUIT while the PIPE is open? *I don't see this behavior
> > documented anywhere. *
> > I expect the %SIG settings to be obeyed as
> > documented

>
> What docs are you referring to?
>
> Modern perls can defer the delivery of signals to the %SIG handlers.



> Here it appears that when close() is blocking on a handle attached to
> a child process perl never delivers the INT and QUIT signals.
>


Yes, perl ignores INT and QUIT in the parent so they'll
kill only the child and not the parent. As I think about
my earlier response, that's why an INT to the process
group works and you'll see the close() complete with a
child status set to 2, ie., INT on most Unix systems.

Perl does the same thing with system() calls by design
so the child will terminate and the parent can then
check its status. perldoc -f system.


> In similar cases these signals are delivered: a fork()/wait()
> combination, blocking for IO, etc...


Yes, and, in the case of I/O, the signal delivery
may be deferred though. From perlipc:

With the "deferred" scheme the handler is not
called immediately, and if Perl is using system's
"stdio" library that library may re-start the "read"
without returning to Perl and giving it a chance to
call the %SIG handler.

I also notice that perl now may fail and set $! to
EINTR in the case of restartable system calls like
read according to perlipc. In the OP's case for
instance, sending a SIGTERM to the parent with
a slight tweak to his handler will demo this:

die "got a SIG$signame signal \$!=$!\n";

--> got a SIGTERM signal $!=Interrupted signal call


>
> Maybe this behavior underscores the nice convenience Perl provides via
> a piped open()? Not only do you not have to pipe(), dup(), fork(),
> exec() and wait(), but you can almost always reap your child without
> having to get caught up in the signal world.



downside is that
> > Here's the complete behavior, in detail, when the signal is sent while
> > the PIPE i/o is underway. *In each case, I run the script in a shell,
> > in the foreground.

>
> > SIGINT from keyboard => kills PIPE i/o but does not call signal
> > handler
> > SIGQUIT from keyboard => kills PIPE i/o but does not call signal
> > handler
> > SIGINT from another terminal => has no effect whatsoever
> > SIGQUIT from another terminal => has no effect whatsoever
> > SIGTERM from another terminal => signal handler is called

>
> Given the close() issue above this sounds "right".
>
> The parent ignores all but SIGTERM and, in the first two cases, the
> signal was sent to the process group so the child receives it and the
> parent's close() returns.
>
> > Why is there any difference at all depending on where the signal comes
> > from, and on what signal is sent?

>
> Signals are odd beasts.


Really odd and needs lot of careful reading
of perlipc, etc.

--
Charles DeRykus



All times are GMT. The time now is 05:06 AM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.