summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeon Timmermans <fawaka@gmail.com>2011-01-18 16:40:07 +0100
committerFather Chrysostomos <sprout@cpan.org>2011-01-18 20:44:13 -0800
commit7fe50b8b8a4dc38fc341e3b403545aaca937f50e (patch)
tree386b8e41555c243c365a4f50fb293b70991ae76a
parent2d51747b8d0fbff249ca971199aa8ddb5856d3c3 (diff)
downloadperl-7fe50b8b8a4dc38fc341e3b403545aaca937f50e.tar.gz
Also unblock signal handlers throwing an exception
Also handle and test the edge case of a signal handler throwing an exception
-rw-r--r--mg.c31
-rw-r--r--t/op/sigdispatch.t21
2 files changed, 35 insertions, 17 deletions
diff --git a/mg.c b/mg.c
index f51cd47990..cc135318de 100644
--- a/mg.c
+++ b/mg.c
@@ -1434,6 +1434,14 @@ Perl_csighandler_init(void)
}
#endif
+#if defined HAS_SIGPROCMASK
+static void
+unblock_sigmask(pTHX_ void* newset)
+{
+ sigprocmask(SIG_UNBLOCK, (sigset_t*)newset, NULL);
+}
+#endif
+
void
Perl_despatch_signals(pTHX)
{
@@ -1443,7 +1451,7 @@ Perl_despatch_signals(pTHX)
for (sig = 1; sig < SIG_SIZE; sig++) {
if (PL_psig_pend[sig]) {
dSAVE_ERRNO;
-#if defined(HAS_SIGPROCMASK)
+#ifdef HAS_SIGPROCMASK
/* From sigaction(2) (FreeBSD man page):
* | Signal routines normally execute with the signal that
* | caused their invocation blocked, but other signals may
@@ -1458,6 +1466,12 @@ Perl_despatch_signals(pTHX)
sigaddset(&newset, sig);
sigprocmask(SIG_BLOCK, &newset, &oldset);
was_blocked = sigismember(&oldset, sig);
+ if (!was_blocked) {
+ SV* save_sv = newSVpvn((char *)(&newset), sizeof(sigset_t));
+ ENTER;
+ SAVEFREESV(save_sv);
+ SAVEDESTRUCTOR_X(unblock_sigmask, SvPV_nolen(save_sv));
+ }
#endif
PL_psig_pend[sig] = 0;
#if defined(HAS_SIGACTION) && defined(SA_SIGINFO)
@@ -1465,9 +1479,9 @@ Perl_despatch_signals(pTHX)
#else
(*PL_sighandlerp)(sig);
#endif
-#if defined(HAS_SIGPROCMASK)
+#ifdef HAS_SIGPROCMASK
if (!was_blocked)
- sigprocmask(SIG_UNBLOCK, &newset, NULL);
+ LEAVE;
#endif
RESTORE_ERRNO;
}
@@ -3092,22 +3106,15 @@ Perl_sighandler(int sig)
POPSTACK;
if (SvTRUE(ERRSV)) {
-#ifndef PERL_MICRO
-#ifdef HAS_SIGPROCMASK
+#if !defined(PERL_MICRO) && !defined(HAS_SIGPROCMASK)
/* Handler "died", for example to get out of a restart-able read().
* Before we re-do that on its behalf re-enable the signal which was
* blocked by the system when we entered.
*/
- sigset_t set;
- sigemptyset(&set);
- sigaddset(&set,sig);
- sigprocmask(SIG_UNBLOCK, &set, NULL);
-#else
/* Not clear if this will work */
(void)rsignal(sig, SIG_IGN);
(void)rsignal(sig, PL_csighandlerp);
-#endif
-#endif /* !PERL_MICRO */
+#endif /* !PERL_MICRO && !HAS_SIGPROCMASK*/
die_sv(ERRSV);
}
cleanup:
diff --git a/t/op/sigdispatch.t b/t/op/sigdispatch.t
index a86861e1af..e3c8fdb110 100644
--- a/t/op/sigdispatch.t
+++ b/t/op/sigdispatch.t
@@ -9,7 +9,7 @@ BEGIN {
use strict;
use Config;
-plan tests => 9;
+plan tests => 12;
watchdog(10);
@@ -39,7 +39,7 @@ eval {
is($@, "Alarm!\n", 'after the second loop');
SKIP: {
- skip('We can\'t test blocking without sigprocmask', 3) if $ENV{PERL_CORE_MINITEST} || !$Config{d_sigprocmask};
+ skip('We can\'t test blocking without sigprocmask', 8) if $ENV{PERL_CORE_MINITEST} || !$Config{d_sigprocmask};
require POSIX;
my $new = POSIX::SigSet->new(&POSIX::SIGUSR1);
@@ -48,15 +48,26 @@ SKIP: {
my $gotit = 0;
$SIG{USR1} = sub { $gotit++ };
kill SIGUSR1, $$;
- is $gotit, 0, 'Haven\'t third received signal yet';
+ is $gotit, 0, 'Haven\'t received third signal yet';
my $old = POSIX::SigSet->new();
POSIX::sigsuspend($old);
is $gotit, 1, 'Received third signal';
+ {
+ kill SIGUSR1, $$;
+ local $SIG{USR1} = sub { die "FAIL\n" };
+ POSIX::sigprocmask(&POSIX::SIG_BLOCK, undef, $old);
+ ok $old->ismember(&POSIX::SIGUSR1), 'SIGUSR1 is blocked';
+ eval { POSIX::sigsuspend(POSIX::SigSet->new) };
+ is $@, "FAIL\n", 'Exception is thrown, so received fourth signal';
+ POSIX::sigprocmask(&POSIX::SIG_BLOCK, undef, $old);
+ ok $old->ismember(&POSIX::SIGUSR1), 'SIGUSR1 is still blocked';
+ }
+
kill SIGUSR1, $$;
- is $gotit, 1, 'Haven\'t fourth received signal yet';
+ is $gotit, 1, 'Haven\'t received fifth signal yet';
POSIX::sigprocmask(&POSIX::SIG_UNBLOCK, $new, $old);
ok $old->ismember(&POSIX::SIGUSR1), 'SIGUSR1 was still blocked';
- is $gotit, 2, 'Received fourth signal';
+ is $gotit, 2, 'Received fifth signal';
}