3

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?

Borodin
  • 126,100
  • 9
  • 70
  • 144
gxtaillon
  • 1,016
  • 1
  • 19
  • 33
  • works fine on 5.24 (it seems). Don't have time to test, but what happens if you put the sig handler in a `BEGIN` block on 5.10... does that change anything? I hope and think not... – stevieb Nov 19 '16 at 03:12
  • I've tested it again with 5.22 at home and I've got the same behavior. Launch then Ctrl-C -> Caught..., with end block: . As for the BEGIN block, on 5.22, setting the handler there get signals caught, but with an END block too, signals are still uncaught. – gxtaillon Nov 19 '16 at 03:29
  • You're absolutely right here. I was mistaken. The sig handler does not catch properly, even in 5.24 as I said earlier. I thought I've done this in code before, but somehow, I'm starting to doubt it as I can't get it to work on several perlbrew instances. – stevieb Nov 19 '16 at 03:33
  • "Note Signals that are sent to your script can bypass the END blocks." perhaps there's a misunderstanding/misinterpretation there. If sigs can "bypass" END blocks, then maybe this makes sense. Could "bypass" here mean 'override'? – stevieb Nov 19 '16 at 03:39
  • out of curiosity, what is your objective? I'm not going all XY on you as you've raised something interesting, but what is it you're trying to achieve? – stevieb Nov 19 '16 at 03:41
  • Wouldn't there be red flags everywhere in the manual if this was intended behaviour, or at least a mention, somewhere, that this occurs? The objective was centralised children management in a script that forks lots of children and grand children in lots of use cases. It seemed to make sense to wait for children there. My mistake was committing lots of work at once and having to backtrack to the real issue.... – gxtaillon Nov 19 '16 at 03:42
  • well, probably not. END blocks are executed at the end of everything, so perhaps, ^C (interrupt) kills the proc before END can be executed. I'm now starting to think logically that this is designed behaviour. I hope someone else can speak up here – stevieb Nov 19 '16 at 03:45
  • 1
    You seem to feel personally wronged. No one picked you out. Perl is a programming language, and you would get a better answer much more quickly if you got alongside the rest of us who trust and respect the language but occasionally find a bug that needs to be fixed. In this case I am not surprised that signals are disabled by an `END` block, otherwise it could lead to an indefinite cycle. Are you asking for documentation of the change between versions of Perl? – Borodin Nov 19 '16 at 04:03
  • 1
    I should explain that I had edited your question to be much less strident, and my criticisms relate to your original post. – Borodin Nov 19 '16 at 04:10
  • I understand your concerns but a great programming language, and I have not found one like that yet, would stop you before doing implicit changes, introducing inconsistencies in your program and generally from shooting yourself in the foot. Here why would there be an indefinite cycle? Resetting the handler produces the behaviour I would have expected initially. Yes the END blocks run last but they're still a part of the program which always stops to let signal handlers run. The precedence seems well defined. – gxtaillon Nov 19 '16 at 04:23
  • @gxtaillon This is a good question. But before we get to discussion of behavior as it is, may I please ask for the purpose -- did you want to reap kids in END blocks (judged by your comment)? Or, as in the question, to defend against other signals (since you may have long cleanup procedure, for example)? This is really of only importance and a clarification would help us help you out effectively. Sorry about the frustration. But, as we know, there is no perfect anything, and no perfect tool, and most certainly no perfect programming language. – zdim Nov 19 '16 at 08:09
  • 1
    @gxtaillon: Larry Wall, the creator of Perl, wrote, *"Perl doesn't have an infatuation with enforced privacy. It would prefer that you stayed out of its living room because you weren't invited, not because it has a shotgun"* – Borodin Nov 19 '16 at 09:50

2 Answers2

3

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 "SIGINT: got zap!" };

#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 "SIGINT in END block, got zap" };
}

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

done.
END, runs next to last. Sleep then print
^CSIGINT in END block, got zap
Woke up.
END, runs last

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

It appears that the change to "END" ${^GLOBAL_PHASE} removes or otherwise disables the handler.

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.

I will update when (if) I understand more detail and find documentation on this behavior.

zdim
  • 64,580
  • 5
  • 52
  • 81
1

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.

Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339