Add Thread::Signal to run signal handlers reliably in a new thread
Malcolm Beattie [Wed, 18 Mar 1998 11:03:11 +0000 (11:03 +0000)]
p4raw-id: //depot/perl@835

MANIFEST
ext/Thread/Thread.xs
ext/Thread/Thread/Signal.pm [new file with mode: 0644]

index 004e350..92f4001 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -267,6 +267,7 @@ ext/Thread/Thread.pm        Thread extension Perl module
 ext/Thread/Thread.xs   Thread extension external subroutines
 ext/Thread/Thread/Queue.pm     Thread synchronised queue objects
 ext/Thread/Thread/Semaphore.pm Thread semaphore objects
+ext/Thread/Thread/Signal.pm    Start a thread to run signal handlers
 ext/Thread/Thread/Specific.pm  Thread specific data access
 ext/Thread/create.t    Test thread creation
 ext/Thread/die.t       Test thread die()
index aea72f4..b867fec 100644 (file)
@@ -280,8 +280,15 @@ static Signal_t handle_thread_signal _((int sig));
 static Signal_t
 handle_thread_signal(int sig)
 {
-    char c = (char) sig;
-    write(sig_pipe[0], &c, 1);
+    unsigned char c = (unsigned char) sig;
+    /*
+     * We're not really allowed to call fprintf in a signal handler
+     * so don't be surprised if this isn't robust while debugging
+     * with -DL.
+     */
+    DEBUG_L(PerlIO_printf(PerlIO_stderr(),
+           "handle_thread_signal: got signal %d\n", sig););
+    write(sig_pipe[1], &c, 1);
 }
 
 MODULE = Thread                PACKAGE = Thread
@@ -555,7 +562,7 @@ MODULE = Thread             PACKAGE = Thread::Signal
 void
 kill_sighandler_thread()
     PPCODE:
-       write(sig_pipe[0], "\0", 1);
+       write(sig_pipe[1], "\0", 1);
        PUSHs(&sv_yes);
 
 void
@@ -566,22 +573,22 @@ init_thread_signals()
            XSRETURN_UNDEF;
        PUSHs(&sv_yes);
 
-SV *
+void
 await_signal()
     PREINIT:
-       char c;
+       unsigned char c;
        SSize_t ret;
     CODE:
        do {
-           ret = read(sig_pipe[1], &c, 1);
+           ret = read(sig_pipe[0], &c, 1);
        } while (ret == -1 && errno == EINTR);
        if (ret == -1)
            croak("panic: await_signal");
-       if (ret == 0)
-           XSRETURN_UNDEF;
-       RETVAL = c ? psig_ptr[c] : &sv_no;
-    OUTPUT:
-       RETVAL
+       ST(0) = sv_newmortal();
+       if (ret)
+           sv_setsv(ST(0), c ? psig_ptr[c] : &sv_no);
+       DEBUG_L(PerlIO_printf(PerlIO_stderr(),
+                             "await_signal returning %s\n", SvPEEK(ST(0))););
 
 MODULE = Thread                PACKAGE = Thread::Specific
 
diff --git a/ext/Thread/Thread/Signal.pm b/ext/Thread/Thread/Signal.pm
new file mode 100644 (file)
index 0000000..f5f03db
--- /dev/null
@@ -0,0 +1,50 @@
+package Thread::Signal;
+use Thread qw(async);
+
+=head1 NAME
+
+Thread::Signal - Start a thread which runs signal handlers reliably
+
+=head1 SYNOPSIS
+
+    use Thread::Signal;
+
+    $SIG{HUP} = \&some_handler;
+
+=head1 DESCRIPTION
+
+The C<Thread::Signal> module starts up a special signal handler thread.
+All signals to the process are delivered to it and it runs the
+associated C<$SIG{FOO}> handlers for them. Without this module,
+signals arriving at inopportune moments (such as when perl's internals
+are in the middle of updating critical structures) cause the perl
+code of the handler to be run unsafely which can cause memory corruption
+or worse.
+
+=head1 BUGS
+
+This module changes the semantics of signal handling slightly in that
+the signal handler is run separately from the main thread (and in
+parallel with it). This means that tricks such as calling C<die> from
+a signal handler behave differently (and, in particular, can't be
+used to exit directly from a system call).
+
+=cut
+
+if (!init_thread_signals()) {
+    require Carp;
+    Carp::croak("init_thread_signals failed: $!");
+}
+
+async {
+    my $sig;
+    while ($sig = await_signal()) {
+       &$sig();
+    }
+};
+
+END {
+    kill_sighandler_thread();
+}
+
+1;