Perl signal handlers are reset in END blocks

669 Views Asked by At

This works as expected since Perl 5.10.1: SIGINTs are trapped.

#!/usr/bin/perl

use strict;
use warnings;

$SIG{INT} = sub { die "Caught a sigint $!" };

sleep(20);

But here SIGINTs are not trapped.

#!/usr/bin/perl

use strict;
use warnings;

$SIG{INT} = sub { die "Caught a sigint $!" };

END {    
    sleep(20);
}

This can be fixed by setting the handler again in the END block, like so:

END {
    $SIG{INT} = sub { die "Caught a sigint $!" };

    sleep(20);
}

But that won't work if you have more than one block: handlers must be set again for every block.

I've tried to figure this out and I can't find an explanation at perldoc. The only mention of this behaviour I can find is a footnote from Practical Perl Programming A D Marshall 1999-2005

Note Signals that are sent to your script can bypass the END blocks.

Would someone explain this?

2

There are 2 best solutions below

0
On

This works for me: Re-install the handler in the END block that runs first (last in code)

use warnings;
use strict;
use feature 'say';

$SIG{INT} = sub { say "Got $_[0]" };

#sleep 10;

say "done";

END { say "END, runs last" }
END { 
    say "END, runs next to last. Sleep then print"; 
    sleep 10; 
    say "Woke up."; 
}

# Executed first in END phase. The sole purpose: to reinstall the handler
END { 
    $SIG{INT} = sub { say "In END block, got $_[0]" };
}

When started and Ctrl-C-ed after a few seconds this prints

done.
END, runs next to last. Sleep then print
^CIn END block, got INT
Woke up.
END, runs last

So you need to add an END block, last in the code, END { $SIG{INT} = 'IGNORE' }.

The change to END ${^GLOBAL_PHASE}, which comes after the runtime (RUN phase) finished, apparently removes/disables signal handlers. I can't find this in documentation.

But once the handler is re-installed in the END phase it is effective throughout. It's cleanest of course to do that in the END block that is executed first.

0
On

perldoc perlmod says:

An "END" code block is executed as late as possible, that is, after perl has finished running the program and just before the interpreter is being exited, even if it is exiting as a result of a die() function. (But not if it's morphing into another program via "exec", or being blown out of the water by a signal--you have to trap that yourself (if you can).)

I expect signal handlers to have been removed just before the interpreter is being exited. Therefore, I am not quite able to see what is surprising or unanticipated.