Restore errno if signal handler changes it
authorLubomir Rintel <lubo.rintel@gooddata.com>
Mon, 26 Jul 2010 14:05:05 +0000 (16:05 +0200)
committerRafael Garcia-Suarez <rgs@consttype.org>
Mon, 26 Jul 2010 14:06:25 +0000 (16:06 +0200)
It's way too easy to forget to "local $!" in signal handlers and
changing $! when signal hits between two ops is probably never useful.

MANIFEST
mg.c
t/io/errnosig.t [new file with mode: 0644]

index 22b1083..f48cc47 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -4275,6 +4275,7 @@ t/io/crlf_through.t               See if pipe passes data intact with :crlf
 t/io/defout.t                  See if PL_defoutgv works
 t/io/dup.t                     See if >& works right
 t/io/errno.t                   See if $! is correctly set
+t/io/errnosig.t                        Test case for restoration $! when leaving signal handlers
 t/io/fflush.t                  See if auto-flush on fork/exec/system/qx works
 t/io/fs.t                      See if directory manipulations work
 t/io/inplace.t                 See if inplace editing works
diff --git a/mg.c b/mg.c
index 47844e0..5eca2e5 100644 (file)
--- a/mg.c
+++ b/mg.c
@@ -1385,6 +1385,7 @@ Perl_despatch_signals(pTHX)
     PL_sig_pending = 0;
     for (sig = 1; sig < SIG_SIZE; sig++) {
        if (PL_psig_pend[sig]) {
+           dSAVE_ERRNO;
            PERL_BLOCKSIG_ADD(set, sig);
            PL_psig_pend[sig] = 0;
            PERL_BLOCKSIG_BLOCK(set);
@@ -1394,6 +1395,7 @@ Perl_despatch_signals(pTHX)
            (*PL_sighandlerp)(sig);
 #endif
            PERL_BLOCKSIG_UNBLOCK(set);
+           RESTORE_ERRNO;
        }
     }
 }
diff --git a/t/io/errnosig.t b/t/io/errnosig.t
new file mode 100644 (file)
index 0000000..8effcf1
--- /dev/null
@@ -0,0 +1,30 @@
+#!./perl
+
+BEGIN {
+    chdir 't' if -d 't';
+    @INC = qw(. ../lib);
+}
+
+require Config; import Config;
+require "test.pl";
+plan(tests => 1);
+
+SKIP: {
+    skip("Alarm not supported", 1) unless exists $Config{'d_alarm'};
+
+    $SIG{ALRM} = sub {
+        # We could call anything that modifies $! here, but
+        # this way we can be sure that it isn't the same
+        # errno as interrupted sleep() would return, and are
+        # able to check it thereafter.
+        $! = -1;
+    };
+
+    alarm 1;
+    sleep 2;
+
+    # Interrupted sleeps sets errno to EAGAIN, but signal
+    # that # hits after it (if safe signal handling is enabled)
+    # causes a routing that modifies $! to be run afterwards
+    isnt($! + 0, -1, 'Signal does not modify $!');
+}