Why doesn't die in alarm signal handler kill t

2019-07-03 12:23发布

问题:

From How can I specify timeout limit for Perl system call?

eval { 
    local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required 
    alarm $timeout; 
    $nread = sysread SOCKET, $buffer, $size; 
    alarm 0; 
}; 
if ($@) { 
    die unless $@ eq "alarm\n";   # propagate unexpected errors 
    # timed out 
} 
else { 
    # didn't 
} 

If a timeout happens, should sub { die "alarm\n" }; cause the end of a process. I guess I am not able to understand die. This http://www.cs.cf.ac.uk/Dave/PERL/node111.html says that "The die() function is used to quit your script and display a message for the user to read". However, in the case of the script above, the script will process the code in #timed out. Also sysread continues to work. Instead of sysread, I had a perl script that slept for 30 seconds. My timeout was set to 10 seconds. As expected, the code in #timed out is executed but the script continued to sleep.Any inputs appreciated

回答1:

die doesn't cause the end of a process, it throws an exception.

Now, if nothing catches an exception, that ends a process, but you have code in place to catch this very exception.

The process doesn't end because you explicitly prevent it from ending.


Since you're not very clear on what behaviour you are getting, there could be another possibility: That you are using a Windows build of Perl.

The alarm is a Unix system call. It's very purpose (sending a signal after a certain amount of time has passed) makes no sense on Windows since Windows doesn't have signals.

Perl emulates alarm to some extent, but only in a very limited manner. sleep could very well be the only operation that's interruptable by alarm. Otherwise, the timeout is only checked between statements.

So it won't interrupt sysread, but once sysread returns, Perl notices the timeout expired and emulate a signal then.



回答2:

From man alarm

   alarm() arranges for a SIGALRM signal to be delivered to the calling process in seconds seconds.

Before sigalarm is delivered execution reaches else block. Insert a STDIN before sysread so that sigalarm triggers resulting expected results.



回答3:

"Instead of sysread, I had a perl script that slept for 30 seconds. My timeout was set to 10 seconds. As expected, the code in #timed out is executed but the script continued to sleep."

Really?

#!/usr/bin/perl
use strict;
use warnings FATAL => qw(all);

eval {
    open my $fh, '<', $0 || die;
    local $SIG{ALRM} = sub {
        print STDERR "hello!\n";
        die "bye!";
    };
    alarm 3;
    while (<$fh>) {
        print $_;
        sleep 1;
    }
    close $fh;
};

if ($@) {
    print "HERE: $@\n";
}

The output:

#!/usr/bin/perl
use strict;
use warnings FATAL => qw(all);
hello!
HERE: bye! at ./test.pl line 9, <$fh> line 3.

Over in the expected 3 seconds; this is still the case if I just use "sleep 100" instead of the file read. Note that if you spawn a subprocess, alarm will not kill that and the parent process must wait. In that case, the "hello!" in the signal handler will appear when alarm fires, but the eval which catches the die will not complete until the subprocess does.



回答4:

I had the same issue when porting a Linux Perl script to Windows.

I solved it by ...

Creating a non-blocking socket

$recsock = IO::Socket::INET->new(
                             LocalPort => 68,
                             Proto => "udp",
                             Broadcast => 1,
                            Blocking => 0,
                             ) or die "socket: $@";

Adding $continue variable to the timeout handle

# Timeout handle
$SIG{ALRM} = sub { 
    print "timeout\n";
    $continue = 1;
};

and checking for the $continue to become true when the timeout occurs:

    alarm($timeout);
     while(1){
         $recsock->recv($newmsg, 1024);
         eval {
            $packet = Net::Package->new($newmsg); 
             ...
        };
        sleep 0.1;
        last if ($continue);
    }
    alarm(0);


标签: perl die