summaryrefslogtreecommitdiffstats
path: root/signal.c
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2013-12-12 22:47:27 -0800
committerKaz Kylheku <kaz@kylheku.com>2013-12-12 22:47:27 -0800
commitb3f8bee89e3fa5713ff391e0172b8e1d4b92be25 (patch)
tree14d49b10503cd3e086d00c79df184e51d444fe6b /signal.c
parentccf6c78422c227778c5e6103413849803aa7b158 (diff)
downloadtxr-b3f8bee89e3fa5713ff391e0172b8e1d4b92be25.tar.gz
txr-b3f8bee89e3fa5713ff391e0172b8e1d4b92be25.tar.bz2
txr-b3f8bee89e3fa5713ff391e0172b8e1d4b92be25.zip
First cut at signal handling support.
* Makefile (OBJS-y): Include signal.o if have_posix_sigs is "y". * configure (have_posix_sigs): New variable, set by detecting POSIX signal stuff. * dep.mk: Regenerated. * arith.c, debug.c, eval.c, filter.c, hash.c, match.c, parser.y, parser.l, rand.c, regex.c, syslog.c, txr.c, utf8.c: Include new signal.h header, now required by unwind, and the <signal.h> system header. * eval.c (exit_wrap): New function. (eval_init): New functions registered as intrinsics: exit_wrap, set_sig_handler, get_sig_handler, sig_check. * gc.c (release): Unused functions removed. * gc.h (release): Declaration removed. * lib.c (init): Call sig_init. * stream.c (set_putc, se_getc, se_fflush): New static functions. (stdio_put_char_callback, stdio_get_char_callback, stdio_put_byte, stdio_flush, stdio_get_byte): Use new functions to enable signals when blocked on I/O. (tail_strategy): Allow signals across sleep. (pipev_close): Allow signals across waitpid. (se_pclose): New static function. (pipe_close): Use new function to enable signals across pclose. * unwind.c (uw_unwind_to_exit_point): use extended_longjmp instead of longjmp. * unwind.h (struct uw_block, struct uw_catch): jb member changes from jmp_buf to extended_jmp_buf. (uw_block_begin, uw_simple_catch_begin, uw_catch_begin): Use extended_setjmp instead of setjmp. * signal.c: New file. * signal.h: New file.
Diffstat (limited to 'signal.c')
-rw-r--r--signal.c135
1 files changed, 135 insertions, 0 deletions
diff --git a/signal.c b/signal.c
new file mode 100644
index 00000000..05ad9eb6
--- /dev/null
+++ b/signal.c
@@ -0,0 +1,135 @@
+/* Copyright 2013-2014
+ * Kaz Kylheku <kaz@kylheku.com>
+ * Vancouver, Canada
+ * All rights reserved.
+ *
+ * BSD License:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include <wchar.h>
+#include <dirent.h>
+#include <signal.h>
+#include "config.h"
+#include "lib.h"
+#include "stream.h"
+#include "hash.h"
+#include "gc.h"
+#include "signal.h"
+#include "unwind.h"
+
+#define MAX_SIG 32
+
+volatile sig_atomic_t async_sig_enabled = 0;
+
+static val sig_lambda[MAX_SIG];
+volatile unsigned long sig_deferred;
+
+static void sig_handler(int sig)
+{
+ val lambda = sig_lambda[sig];
+ if (lambda) {
+ if (async_sig_enabled) {
+ async_sig_enabled = 0;
+ funcall1(lambda, num_fast(sig));
+ async_sig_enabled = 1;
+ } else {
+ sig_deferred |= (1UL << sig);
+ }
+ }
+}
+
+void sig_init(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_SIG; i++) {
+ /* t means SIG_DFL, which is what signals
+ * are before we manipulate them. */
+ sig_lambda[i] = t;
+ prot1(&sig_lambda[i]);
+ }
+}
+
+val set_sig_handler(val signo, val lambda)
+{
+ cnum sig = c_num(signo);
+ val old_lambda;
+
+ if (sig < 0 || sig >= MAX_SIG)
+ uw_throwf(error_s, lit("set-sig-handler: signal ~s out of range\n"), sig, nao);
+
+ old_lambda = sig_lambda[sig];
+
+ if (lambda != old_lambda) {
+ if (lambda == nil) {
+ signal(sig, SIG_IGN);
+ } else if (lambda == t) {
+ signal(sig, SIG_DFL);
+ } else {
+ struct sigaction sa = { 0 };
+
+ type_check(lambda, FUN);
+
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = sig_handler;
+ sigfillset(&sa.sa_mask);
+ sigaction(sig, &sa, 0);
+ }
+
+ sig_lambda[sig] = lambda;
+ }
+
+ return old_lambda;
+}
+
+val get_sig_handler(val signo)
+{
+ cnum sig = c_num(signo);
+
+ if (sig < 0 || sig >= MAX_SIG)
+ uw_throwf(error_s, lit("get-sig-handler: signal ~s out of range\n"), sig, nao);
+
+ return sig_lambda[sig];
+}
+
+val sig_check(void)
+{
+ unsigned long sd;
+ int i;
+
+ if ((sd = sig_deferred) == 0)
+ return nil;
+
+ for (i = 0; i < MAX_SIG; i++) {
+ unsigned long mask = 1UL << i;
+ if ((sd & mask) != 0) {
+ sd &= ~mask;
+ sig_deferred = sd;
+ funcall1(sig_lambda[i], num_fast(i));
+ }
+ }
+
+ return t;
+}