summaryrefslogtreecommitdiff
path: root/ext/Thread
diff options
context:
space:
mode:
authorMalcolm Beattie <mbeattie@sable.ox.ac.uk>1998-03-18 11:03:11 +0000
committerMalcolm Beattie <mbeattie@sable.ox.ac.uk>1998-03-18 11:03:11 +0000
commit3aeed370afcacdc526e9f8575b34e519df1ffe51 (patch)
tree5864e71f855b4dc0b422c4e4a2806bef9bc6edf5 /ext/Thread
parent63c303421ae8994f77ab63c42697d3066f2fd98a (diff)
downloadperl-3aeed370afcacdc526e9f8575b34e519df1ffe51.tar.gz
Add Thread::Signal to run signal handlers reliably in a new thread
p4raw-id: //depot/perl@835
Diffstat (limited to 'ext/Thread')
-rw-r--r--ext/Thread/Thread.xs29
-rw-r--r--ext/Thread/Thread/Signal.pm50
2 files changed, 68 insertions, 11 deletions
diff --git a/ext/Thread/Thread.xs b/ext/Thread/Thread.xs
index aea72f4a46..b867fec72b 100644
--- a/ext/Thread/Thread.xs
+++ b/ext/Thread/Thread.xs
@@ -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
index 0000000000..f5f03db8a8
--- /dev/null
+++ b/ext/Thread/Thread/Signal.pm
@@ -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;