1.0.41.21: runtime: Current stack and frame pointers are per-thread data.
[sbcl.git] / src / runtime / interrupt.c
index ad53a4c..ea0880f 100644 (file)
 #include "globals.h"
 #include "lispregs.h"
 #include "validate.h"
+#include "interr.h"
 #include "gc.h"
 #include "alloc.h"
 #include "dynbind.h"
-#include "interr.h"
 #include "pseudo-atomic.h"
 #include "genesis/fdefn.h"
 #include "genesis/simple-fun.h"
 #include "genesis/cons.h"
 
-static void run_deferred_handler(struct interrupt_data *data, void *v_context);
+/* When we catch an internal error, should we pass it back to Lisp to
+ * be handled in a high-level way? (Early in cold init, the answer is
+ * 'no', because Lisp is still too brain-dead to handle anything.
+ * After sufficient initialization has been completed, the answer
+ * becomes 'yes'.) */
+boolean internal_errors_enabled = 0;
+
+#ifndef LISP_FEATURE_WIN32
+static
+void (*interrupt_low_level_handlers[NSIG]) (int, siginfo_t*, os_context_t*);
+#endif
+union interrupt_handler interrupt_handlers[NSIG];
+
+/* Under Linux on some architectures, we appear to have to restore the
+ * FPU control word from the context, as after the signal is delivered
+ * we appear to have a null FPU control word. */
+#if defined(RESTORE_FP_CONTROL_FROM_CONTEXT)
+#define RESTORE_FP_CONTROL_WORD(context,void_context)           \
+    os_context_t *context = arch_os_get_context(&void_context); \
+    os_restore_fp_control(context);
+#else
+#define RESTORE_FP_CONTROL_WORD(context,void_context)           \
+    os_context_t *context = arch_os_get_context(&void_context);
+#endif
+
+/* Foreign code may want to start some threads on its own.
+ * Non-targetted, truly asynchronous signals can be delivered to
+ * basically any thread, but invoking Lisp handlers in such foregign
+ * threads is really bad, so let's resignal it.
+ *
+ * This should at least bring attention to the problem, but it cannot
+ * work for SIGSEGV and similar. It is good enough for timers, and
+ * maybe all deferrables. */
+
+#ifdef LISP_FEATURE_SB_THREAD
+static void
+add_handled_signals(sigset_t *sigset)
+{
+    int i;
+    for(i = 1; i < NSIG; i++) {
+        if (!(ARE_SAME_HANDLER(interrupt_low_level_handlers[i], SIG_DFL)) ||
+            !(ARE_SAME_HANDLER(interrupt_handlers[i].c, SIG_DFL))) {
+            sigaddset(sigset, i);
+        }
+    }
+}
+
+void block_signals(sigset_t *what, sigset_t *where, sigset_t *old);
+#endif
+
+static boolean
+maybe_resignal_to_lisp_thread(int signal, os_context_t *context)
+{
+#ifdef LISP_FEATURE_SB_THREAD
+    if (!pthread_getspecific(lisp_thread)) {
+        if (!(sigismember(&deferrable_sigset,signal))) {
+            corruption_warning_and_maybe_lose
+                ("Received signal %d in non-lisp thread %lu, resignalling to a lisp thread.",
+                 signal,
+                 pthread_self());
+        }
+        {
+            sigset_t sigset;
+            sigemptyset(&sigset);
+            add_handled_signals(&sigset);
+            block_signals(&sigset, 0, 0);
+            block_signals(&sigset, os_context_sigmask_addr(context), 0);
+            kill(getpid(), signal);
+        }
+        return 1;
+    } else
+#endif
+        return 0;
+}
+
+/* These are to be used in signal handlers. Currently all handlers are
+ * called from one of:
+ *
+ * interrupt_handle_now_handler
+ * maybe_now_maybe_later
+ * unblock_me_trampoline
+ * low_level_handle_now_handler
+ * low_level_maybe_now_maybe_later
+ * low_level_unblock_me_trampoline
+ *
+ * This gives us a single point of control (or six) over errno, fp
+ * control word, and fixing up signal context on sparc.
+ *
+ * The SPARC/Linux platform doesn't quite do signals the way we want
+ * them done. The third argument in the handler isn't filled in by the
+ * kernel properly, so we fix it up ourselves in the
+ * arch_os_get_context(..) function. -- CSR, 2002-07-23
+ */
+#define SAVE_ERRNO(signal,context,void_context)                 \
+    {                                                           \
+        int _saved_errno = errno;                               \
+        RESTORE_FP_CONTROL_WORD(context,void_context);          \
+        if (!maybe_resignal_to_lisp_thread(signal, context))    \
+        {
+
+#define RESTORE_ERRNO                                           \
+        }                                                       \
+        errno = _saved_errno;                                   \
+    }
+
+static void run_deferred_handler(struct interrupt_data *data,
+                                 os_context_t *context);
 #ifndef LISP_FEATURE_WIN32
 static void store_signal_data_for_later (struct interrupt_data *data,
                                          void *handler, int signal,
                                          siginfo_t *info,
                                          os_context_t *context);
+\f
 
-static void
-fill_current_sigmask(sigset_t *sigset)
+/* Generic signal related utilities. */
+
+void
+get_current_sigmask(sigset_t *sigset)
 {
     /* Get the current sigmask, by blocking the empty set. */
-    sigset_t empty;
-    sigemptyset(&empty);
-    thread_sigmask(SIG_BLOCK, &empty, sigset);
+    thread_sigmask(SIG_BLOCK, 0, sigset);
+}
+
+void
+block_signals(sigset_t *what, sigset_t *where, sigset_t *old)
+{
+    if (where) {
+        int i;
+        if (old)
+            sigcopyset(old, where);
+        for(i = 1; i < NSIG; i++) {
+            if (sigismember(what, i))
+                sigaddset(where, i);
+        }
+    } else {
+        thread_sigmask(SIG_BLOCK, what, old);
+    }
 }
 
 void
+unblock_signals(sigset_t *what, sigset_t *where, sigset_t *old)
+{
+    if (where) {
+        int i;
+        if (old)
+            sigcopyset(old, where);
+        for(i = 1; i < NSIG; i++) {
+            if (sigismember(what, i))
+                sigdelset(where, i);
+        }
+    } else {
+        thread_sigmask(SIG_UNBLOCK, what, old);
+    }
+}
+
+static void
+print_sigset(sigset_t *sigset)
+{
+  int i;
+  for(i = 1; i < NSIG; i++) {
+    if (sigismember(sigset, i))
+      fprintf(stderr, "Signal %d masked\n", i);
+  }
+}
+
+/* Return 1 is all signals is sigset2 are masked in sigset, return 0
+ * if all re unmasked else die. Passing NULL for sigset is a shorthand
+ * for the current sigmask. */
+boolean
+all_signals_blocked_p(sigset_t *sigset, sigset_t *sigset2,
+                                const char *name)
+{
+#if !defined(LISP_FEATURE_WIN32)
+    int i;
+    boolean has_blocked = 0, has_unblocked = 0;
+    sigset_t current;
+    if (sigset == 0) {
+        get_current_sigmask(&current);
+        sigset = &current;
+    }
+    for(i = 1; i < NSIG; i++) {
+        if (sigismember(sigset2, i)) {
+            if (sigismember(sigset, i))
+                has_blocked = 1;
+            else
+                has_unblocked = 1;
+        }
+    }
+    if (has_blocked && has_unblocked) {
+        print_sigset(sigset);
+        lose("some %s signals blocked, some unblocked\n", name);
+    }
+    if (has_blocked)
+        return 1;
+    else
+        return 0;
+#endif
+}
+\f
+
+/* Deferrables, blockables, gc signals. */
+
+void
 sigaddset_deferrable(sigset_t *s)
 {
     sigaddset(s, SIGHUP);
@@ -104,35 +290,6 @@ sigaddset_deferrable(sigset_t *s)
     sigaddset(s, SIGVTALRM);
     sigaddset(s, SIGPROF);
     sigaddset(s, SIGWINCH);
-
-#ifdef LISP_FEATURE_SB_THREAD
-    sigaddset(s, SIG_INTERRUPT_THREAD);
-#endif
-}
-
-void
-sigdelset_deferrable(sigset_t *s)
-{
-    sigdelset(s, SIGHUP);
-    sigdelset(s, SIGINT);
-    sigdelset(s, SIGQUIT);
-    sigdelset(s, SIGPIPE);
-    sigdelset(s, SIGALRM);
-    sigdelset(s, SIGURG);
-    sigdelset(s, SIGTSTP);
-    sigdelset(s, SIGCHLD);
-    sigdelset(s, SIGIO);
-#ifndef LISP_FEATURE_HPUX
-    sigdelset(s, SIGXCPU);
-    sigdelset(s, SIGXFSZ);
-#endif
-    sigdelset(s, SIGVTALRM);
-    sigdelset(s, SIGPROF);
-    sigdelset(s, SIGWINCH);
-
-#ifdef LISP_FEATURE_SB_THREAD
-    sigdelset(s, SIG_INTERRUPT_THREAD);
-#endif
 }
 
 void
@@ -150,102 +307,160 @@ sigaddset_gc(sigset_t *sigset)
 #endif
 }
 
-void
-sigdelset_gc(sigset_t *sigset)
-{
-#ifdef LISP_FEATURE_SB_THREAD
-    sigdelset(sigset,SIG_STOP_FOR_GC);
-#endif
-}
-
 /* initialized in interrupt_init */
 sigset_t deferrable_sigset;
 sigset_t blockable_sigset;
 sigset_t gc_sigset;
+
 #endif
 
+#if !defined(LISP_FEATURE_WIN32)
 boolean
-deferrables_blocked_in_sigset_p(sigset_t *sigset)
+deferrables_blocked_p(sigset_t *sigset)
+{
+    return all_signals_blocked_p(sigset, &deferrable_sigset, "deferrable");
+}
+#endif
+
+void
+check_deferrables_unblocked_or_lose(sigset_t *sigset)
 {
 #if !defined(LISP_FEATURE_WIN32)
-    int i;
-    for(i = 1; i < NSIG; i++) {
-        if (sigismember(&deferrable_sigset, i) && sigismember(sigset, i))
-            return 1;
-    }
+    if (deferrables_blocked_p(sigset))
+        lose("deferrables blocked\n");
 #endif
-    return 0;
 }
 
 void
-check_deferrables_unblocked_in_sigset_or_lose(sigset_t *sigset)
+check_deferrables_blocked_or_lose(sigset_t *sigset)
 {
 #if !defined(LISP_FEATURE_WIN32)
-    int i;
-    for(i = 1; i < NSIG; i++) {
-        if (sigismember(&deferrable_sigset, i) && sigismember(sigset, i))
-            lose("deferrable signal %d blocked\n",i);
-    }
+    if (!deferrables_blocked_p(sigset))
+        lose("deferrables unblocked\n");
 #endif
 }
 
+#if !defined(LISP_FEATURE_WIN32)
+boolean
+blockables_blocked_p(sigset_t *sigset)
+{
+    return all_signals_blocked_p(sigset, &blockable_sigset, "blockable");
+}
+#endif
+
 void
-check_deferrables_blocked_in_sigset_or_lose(sigset_t *sigset)
+check_blockables_unblocked_or_lose(sigset_t *sigset)
 {
 #if !defined(LISP_FEATURE_WIN32)
-    int i;
-    for(i = 1; i < NSIG; i++) {
-        if (sigismember(&deferrable_sigset, i) && !sigismember(sigset, i))
-            lose("deferrable signal %d not blocked\n",i);
-    }
+    if (blockables_blocked_p(sigset))
+        lose("blockables blocked\n");
 #endif
 }
 
 void
-check_deferrables_blocked_or_lose(void)
+check_blockables_blocked_or_lose(sigset_t *sigset)
 {
 #if !defined(LISP_FEATURE_WIN32)
-    sigset_t current;
-    fill_current_sigmask(&current);
-    check_deferrables_blocked_in_sigset_or_lose(&current);
+    if (!blockables_blocked_p(sigset))
+        lose("blockables unblocked\n");
 #endif
 }
 
+#if !defined(LISP_FEATURE_WIN32)
+boolean
+gc_signals_blocked_p(sigset_t *sigset)
+{
+    return all_signals_blocked_p(sigset, &gc_sigset, "gc");
+}
+#endif
+
 void
-check_blockables_blocked_or_lose(void)
+check_gc_signals_unblocked_or_lose(sigset_t *sigset)
 {
 #if !defined(LISP_FEATURE_WIN32)
-    sigset_t current;
-    int i;
-    fill_current_sigmask(&current);
-    for(i = 1; i < NSIG; i++) {
-        if (sigismember(&blockable_sigset, i) && !sigismember(&current, i))
-            lose("blockable signal %d not blocked\n",i);
-    }
+    if (gc_signals_blocked_p(sigset))
+        lose("gc signals blocked\n");
 #endif
 }
 
 void
-check_gc_signals_unblocked_in_sigset_or_lose(sigset_t *sigset)
+check_gc_signals_blocked_or_lose(sigset_t *sigset)
 {
 #if !defined(LISP_FEATURE_WIN32)
-    int i;
-    for(i = 1; i < NSIG; i++) {
-        if (sigismember(&gc_sigset, i) && sigismember(sigset, i))
-            lose("gc signal %d blocked\n",i);
-    }
+    if (!gc_signals_blocked_p(sigset))
+        lose("gc signals unblocked\n");
 #endif
 }
 
 void
-check_gc_signals_unblocked_or_lose(void)
+block_deferrable_signals(sigset_t *where, sigset_t *old)
 {
-#if !defined(LISP_FEATURE_WIN32)
-    sigset_t current;
-    fill_current_sigmask(&current);
-    check_gc_signals_unblocked_in_sigset_or_lose(&current);
+#ifndef LISP_FEATURE_WIN32
+    block_signals(&deferrable_sigset, where, old);
+#endif
+}
+
+void
+block_blockable_signals(sigset_t *where, sigset_t *old)
+{
+#ifndef LISP_FEATURE_WIN32
+    block_signals(&blockable_sigset, where, old);
+#endif
+}
+
+void
+block_gc_signals(sigset_t *where, sigset_t *old)
+{
+#ifndef LISP_FEATURE_WIN32
+    block_signals(&gc_sigset, where, old);
+#endif
+}
+
+void
+unblock_deferrable_signals(sigset_t *where, sigset_t *old)
+{
+#ifndef LISP_FEATURE_WIN32
+    if (interrupt_handler_pending_p())
+        lose("unblock_deferrable_signals: losing proposition\n");
+    check_gc_signals_unblocked_or_lose(where);
+    unblock_signals(&deferrable_sigset, where, old);
+#endif
+}
+
+void
+unblock_blockable_signals(sigset_t *where, sigset_t *old)
+{
+#ifndef LISP_FEATURE_WIN32
+    unblock_signals(&blockable_sigset, where, old);
+#endif
+}
+
+void
+unblock_gc_signals(sigset_t *where, sigset_t *old)
+{
+#ifndef LISP_FEATURE_WIN32
+    unblock_signals(&gc_sigset, where, old);
+#endif
+}
+
+void
+unblock_signals_in_context_and_maybe_warn(os_context_t *context)
+{
+#ifndef LISP_FEATURE_WIN32
+    sigset_t *sigset = os_context_sigmask_addr(context);
+    if (all_signals_blocked_p(sigset, &gc_sigset, "gc")) {
+        corruption_warning_and_maybe_lose(
+"Enabling blocked gc signals to allow returning to Lisp without risking\n\
+gc deadlocks. Since GC signals are only blocked in signal handlers when \n\
+they are not safe to interrupt at all, this is a pretty severe occurrence.\n");
+        unblock_gc_signals(sigset, 0);
+    }
+    if (!interrupt_handler_pending_p()) {
+        unblock_deferrable_signals(sigset, 0);
+    }
 #endif
 }
+\f
 
 inline static void
 check_interrupts_enabled_or_lose(os_context_t *context)
@@ -265,12 +480,13 @@ check_interrupts_enabled_or_lose(os_context_t *context)
 void
 maybe_save_gc_mask_and_block_deferrables(sigset_t *sigset)
 {
+#ifndef LISP_FEATURE_WIN32
     struct thread *thread = arch_os_get_current_thread();
     struct interrupt_data *data = thread->interrupt_data;
     sigset_t oldset;
     /* Obviously, this function is called when signals may not be
      * blocked. Let's make sure we are not interrupted. */
-    thread_sigmask(SIG_BLOCK, &blockable_sigset, &oldset);
+    block_blockable_signals(0, &oldset);
 #ifndef LISP_FEATURE_SB_THREAD
     /* With threads a SIG_STOP_FOR_GC and a normal GC may also want to
      * block. */
@@ -292,11 +508,12 @@ maybe_save_gc_mask_and_block_deferrables(sigset_t *sigset)
              * unblock gc signals. In the end, this is equivalent to
              * blocking the deferrables. */
             sigcopyset(&data->pending_mask, &oldset);
-            unblock_gc_signals();
+            thread_sigmask(SIG_UNBLOCK, &gc_sigset, 0);
             return;
         }
     }
     thread_sigmask(SIG_SETMASK,&oldset,0);
+#endif
 }
 
 /* Are we leaving WITH-GCING and already running with interrupts
@@ -319,6 +536,7 @@ in_leaving_without_gcing_race_p(struct thread *thread)
 void
 check_interrupt_context_or_lose(os_context_t *context)
 {
+#ifndef LISP_FEATURE_WIN32
     struct thread *thread = arch_os_get_current_thread();
     struct interrupt_data *data = thread->interrupt_data;
     int interrupt_deferred_p = (data->pending_handler != 0);
@@ -350,7 +568,14 @@ check_interrupt_context_or_lose(os_context_t *context)
         if (stop_for_gc_pending)
             if (!(pseudo_atomic_interrupted || gc_inhibit || in_race_p))
                 lose("STOP_FOR_GC_PENDING, but why?\n");
+        if (pseudo_atomic_interrupted)
+            if (!(gc_pending || stop_for_gc_pending || interrupt_deferred_p))
+                lose("pseudo_atomic_interrupted, but why?\n");
     }
+#else
+    if (pseudo_atomic_interrupted)
+        if (!(gc_pending || interrupt_deferred_p))
+            lose("pseudo_atomic_interrupted, but why?\n");
 #endif
 #endif
     if (interrupt_pending && !interrupt_deferred_p)
@@ -358,99 +583,18 @@ check_interrupt_context_or_lose(os_context_t *context)
     if ((data->gc_blocked_deferrables) && interrupt_pending)
         lose("gc_blocked_deferrables and interrupt pending\n.");
     if (data->gc_blocked_deferrables)
-        check_deferrables_blocked_in_sigset_or_lose(sigset);
-    if (interrupt_pending || interrupt_deferred_p)
-        check_deferrables_blocked_in_sigset_or_lose(sigset);
+        check_deferrables_blocked_or_lose(sigset);
+    if (interrupt_pending || interrupt_deferred_p ||
+        data->gc_blocked_deferrables)
+        check_deferrables_blocked_or_lose(sigset);
     else {
-        check_deferrables_unblocked_in_sigset_or_lose(sigset);
+        check_deferrables_unblocked_or_lose(sigset);
         /* If deferrables are unblocked then we are open to signals
          * that run lisp code. */
-        check_gc_signals_unblocked_in_sigset_or_lose(sigset);
-    }
-}
-
-/* When we catch an internal error, should we pass it back to Lisp to
- * be handled in a high-level way? (Early in cold init, the answer is
- * 'no', because Lisp is still too brain-dead to handle anything.
- * After sufficient initialization has been completed, the answer
- * becomes 'yes'.) */
-boolean internal_errors_enabled = 0;
-
-#ifndef LISP_FEATURE_WIN32
-static void (*interrupt_low_level_handlers[NSIG]) (int, siginfo_t*, void*);
-#endif
-union interrupt_handler interrupt_handlers[NSIG];
-
-void
-block_blockable_signals(void)
-{
-#ifndef LISP_FEATURE_WIN32
-    thread_sigmask(SIG_BLOCK, &blockable_sigset, 0);
-#endif
-}
-
-void
-block_deferrable_signals(void)
-{
-#ifndef LISP_FEATURE_WIN32
-    thread_sigmask(SIG_BLOCK, &deferrable_sigset, 0);
-#endif
-}
-
-void
-unblock_deferrable_signals_in_sigset(sigset_t *sigset)
-{
-#ifndef LISP_FEATURE_WIN32
-    if (interrupt_handler_pending_p())
-        lose("unblock_deferrable_signals_in_sigset: losing proposition\n");
-    check_gc_signals_unblocked_in_sigset_or_lose(sigset);
-    sigdelset_deferrable(sigset);
-#endif
-}
-
-void
-unblock_deferrable_signals(void)
-{
-#ifndef LISP_FEATURE_WIN32
-    if (interrupt_handler_pending_p())
-        lose("unblock_deferrable_signals: losing proposition\n");
-    check_gc_signals_unblocked_or_lose();
-    thread_sigmask(SIG_UNBLOCK, &deferrable_sigset, 0);
-#endif
-}
-
-void
-unblock_gc_signals(void)
-{
-#if defined(LISP_FEATURE_SB_THREAD) && !defined(LISP_FEATURE_WIN32)
-    thread_sigmask(SIG_UNBLOCK,&gc_sigset,0);
-#endif
-}
-
-void
-unblock_signals_in_context_and_maybe_warn(os_context_t *context)
-{
-#ifndef LISP_FEATURE_WIN32
-    int i, oops=0;
-    sigset_t *sigset=os_context_sigmask_addr(context);
-    for(i = 1; i < NSIG; i++) {
-        if (sigismember(&gc_sigset, i) && sigismember(sigset, i)) {
-            if (!oops) {
-                fprintf(stderr,
-"Enabling blocked gc signals to allow returning to Lisp without risking\n\
-gc deadlocks. Since GC signals are only blocked in signal handlers when \n\
-they are not safe to interrupt at all, this is a pretty severe occurrence.\n");
-            }
-            oops=1;
-        }
-    }
-    sigdelset_gc(sigset);
-    if (!interrupt_handler_pending_p()) {
-        unblock_deferrable_signals_in_sigset(sigset);
+        check_gc_signals_unblocked_or_lose(sigset);
     }
 #endif
 }
-
 \f
 /*
  * utility routines used by various signal handlers
@@ -465,23 +609,23 @@ build_fake_control_stack_frames(struct thread *th,os_context_t *context)
 
     /* Build a fake stack frame or frames */
 
-    current_control_frame_pointer =
+    access_control_frame_pointer(th) =
         (lispobj *)(unsigned long)
             (*os_context_register_addr(context, reg_CSP));
     if ((lispobj *)(unsigned long)
             (*os_context_register_addr(context, reg_CFP))
-        == current_control_frame_pointer) {
+        == access_control_frame_pointer(th)) {
         /* There is a small window during call where the callee's
          * frame isn't built yet. */
         if (lowtag_of(*os_context_register_addr(context, reg_CODE))
             == FUN_POINTER_LOWTAG) {
             /* We have called, but not built the new frame, so
              * build it for them. */
-            current_control_frame_pointer[0] =
+            access_control_frame_pointer(th)[0] =
                 *os_context_register_addr(context, reg_OCFP);
-            current_control_frame_pointer[1] =
+            access_control_frame_pointer(th)[1] =
                 *os_context_register_addr(context, reg_LRA);
-            current_control_frame_pointer += 8;
+            access_control_frame_pointer(th) += 8;
             /* Build our frame on top of it. */
             oldcont = (lispobj)(*os_context_register_addr(context, reg_CFP));
         }
@@ -500,11 +644,11 @@ build_fake_control_stack_frames(struct thread *th,os_context_t *context)
         oldcont = (lispobj)(*os_context_register_addr(context, reg_CFP));
     }
 
-    current_control_stack_pointer = current_control_frame_pointer + 8;
+    access_control_stack_pointer(th) = access_control_frame_pointer(th) + 8;
 
-    current_control_frame_pointer[0] = oldcont;
-    current_control_frame_pointer[1] = NIL;
-    current_control_frame_pointer[2] =
+    access_control_frame_pointer(th)[0] = oldcont;
+    access_control_frame_pointer(th)[1] = NIL;
+    access_control_frame_pointer(th)[2] =
         (lispobj)(*os_context_register_addr(context, reg_CODE));
 #endif
 }
@@ -518,12 +662,16 @@ fake_foreign_function_call(os_context_t *context)
     struct thread *thread=arch_os_get_current_thread();
 
     /* context_index incrementing must not be interrupted */
-    check_blockables_blocked_or_lose();
+    check_blockables_blocked_or_lose(0);
 
     /* Get current Lisp state from context. */
 #ifdef reg_ALLOC
+#ifdef LISP_FEATURE_SB_THREAD
+    thread->pseudo_atomic_bits =
+#else
     dynamic_space_free_pointer =
         (lispobj *)(unsigned long)
+#endif
             (*os_context_register_addr(context, reg_ALLOC));
 /*     fprintf(stderr,"dynamic_space_free_pointer: %p\n", */
 /*             dynamic_space_free_pointer); */
@@ -540,9 +688,8 @@ fake_foreign_function_call(os_context_t *context)
 #endif
 #endif
 #ifdef reg_BSP
-    current_binding_stack_pointer =
-        (lispobj *)(unsigned long)
-            (*os_context_register_addr(context, reg_BSP));
+    set_binding_stack_pointer(thread,
+        *os_context_register_addr(context, reg_BSP));
 #endif
 
     build_fake_control_stack_frames(thread,context);
@@ -561,8 +708,11 @@ fake_foreign_function_call(os_context_t *context)
 
     thread->interrupt_contexts[context_index] = context;
 
-#ifdef FOREIGN_FUNCTION_CALL_FLAG
-    foreign_function_call_active = 1;
+#if !defined(LISP_FEATURE_X86) && !defined(LISP_FEATURE_X86_64)
+    /* x86oid targets don't maintain the foreign function call flag at
+     * all, so leave them to believe that they are never in foreign
+     * code. */
+    foreign_function_call_active_p(thread) = 1;
 #endif
 }
 
@@ -574,16 +724,14 @@ undo_fake_foreign_function_call(os_context_t *context)
 {
     struct thread *thread=arch_os_get_current_thread();
     /* Block all blockable signals. */
-    block_blockable_signals();
+    block_blockable_signals(0, 0);
 
-#ifdef FOREIGN_FUNCTION_CALL_FLAG
-    foreign_function_call_active = 0;
-#endif
+    foreign_function_call_active_p(thread) = 0;
 
     /* Undo dynamic binding of FREE_INTERRUPT_CONTEXT_INDEX */
     unbind(thread);
 
-#ifdef reg_ALLOC
+#if defined(reg_ALLOC) && !defined(LISP_FEATURE_SB_THREAD)
     /* Put the dynamic space free pointer back into the context. */
     *os_context_register_addr(context, reg_ALLOC) =
         (unsigned long) dynamic_space_free_pointer
@@ -595,6 +743,17 @@ undo_fake_foreign_function_call(os_context_t *context)
       | ((unsigned long) dynamic_space_free_pointer & LOWTAG_MASK);
     */
 #endif
+#if defined(reg_ALLOC) && defined(LISP_FEATURE_SB_THREAD)
+    /* Put the pseudo-atomic bits and dynamic space free pointer back
+     * into the context (p-a-bits for p-a, and dynamic space free
+     * pointer for ROOM). */
+    *os_context_register_addr(context, reg_ALLOC) =
+        (unsigned long) dynamic_space_free_pointer
+        | (thread->pseudo_atomic_bits & LOWTAG_MASK);
+    /* And clear them so we don't get bit later by call-in/call-out
+     * not updating them. */
+    thread->pseudo_atomic_bits = 0;
+#endif
 }
 
 /* a handler for the signal caused by execution of a trap opcode
@@ -615,7 +774,7 @@ interrupt_internal_error(os_context_t *context, boolean continuable)
 
     /* Allocate the SAP object while the interrupts are still
      * disabled. */
-    unblock_gc_signals();
+    unblock_gc_signals(0, 0);
     context_sap = alloc_sap(context);
 
 #ifndef LISP_FEATURE_WIN32
@@ -633,7 +792,7 @@ interrupt_internal_error(os_context_t *context, boolean continuable)
 #endif
 
     SHOW("in interrupt_internal_error");
-#ifdef QSHOW
+#if QSHOW
     /* Display some rudimentary debugging information about the
      * error, so that even if the Lisp error handler gets badly
      * confused, we have a chance to determine what's going on. */
@@ -675,12 +834,12 @@ interrupt_handle_pending(os_context_t *context)
     struct interrupt_data *data = thread->interrupt_data;
 
     if (arch_pseudo_atomic_atomic(context)) {
-        lose("Handling pending interrupt in pseduo atomic.");
+        lose("Handling pending interrupt in pseudo atomic.");
     }
 
     FSHOW_SIGNAL((stderr, "/entering interrupt_handle_pending\n"));
 
-    check_blockables_blocked_or_lose();
+    check_blockables_blocked_or_lose(0);
 
     /* If GC/SIG_STOP_FOR_GC struck during PA and there was no pending
      * handler, then the pending mask was saved and
@@ -700,7 +859,9 @@ interrupt_handle_pending(os_context_t *context)
          * the os_context for the signal we're currently in the
          * handler for. This should ensure that when we return from
          * the handler the blocked signals are unblocked. */
+#ifndef LISP_FEATURE_WIN32
         sigcopyset(os_context_sigmask_addr(context), &data->pending_mask);
+#endif
         data->gc_blocked_deferrables = 0;
     }
 
@@ -758,7 +919,7 @@ interrupt_handle_pending(os_context_t *context)
             lose("Trapping to run pending handler while GC in progress.");
         }
 
-        check_blockables_blocked_or_lose();
+        check_blockables_blocked_or_lose(0);
 
         /* No GC shall be lost. If SUB_GC triggers another GC then
          * that should be handled on the spot. */
@@ -791,64 +952,41 @@ interrupt_handle_pending(os_context_t *context)
         arch_clear_pseudo_atomic_interrupted(context);
         /* Restore the sigmask in the context. */
         sigcopyset(os_context_sigmask_addr(context), &data->pending_mask);
-        /* This will break on sparc linux: the deferred handler really
-         * wants to be called with a void_context */
-        run_deferred_handler(data,(void *)context);
+        run_deferred_handler(data, context);
     }
+#endif
+#ifdef LISP_FEATURE_GENCGC
+    if (get_pseudo_atomic_interrupted(thread))
+        lose("pseudo_atomic_interrupted after interrupt_handle_pending\n");
+#endif
     /* It is possible that the end of this function was reached
      * without never actually doing anything, the tests in Lisp for
      * when to call receive-pending-interrupt are not exact. */
     FSHOW_SIGNAL((stderr, "/exiting interrupt_handle_pending\n"));
-#endif
 }
 \f
-/*
- * the two main signal handlers:
- *   interrupt_handle_now(..)
- *   maybe_now_maybe_later(..)
- *
- * to which we have added interrupt_handle_now_handler(..).  Why?
- * Well, mostly because the SPARC/Linux platform doesn't quite do
- * signals the way we want them done.  The third argument in the
- * handler isn't filled in by the kernel properly, so we fix it up
- * ourselves in the arch_os_get_context(..) function; however, we only
- * want to do this when we first hit the handler, and not when
- * interrupt_handle_now(..) is being called from some other handler
- * (when the fixup will already have been done). -- CSR, 2002-07-23
- */
 
 void
 interrupt_handle_now(int signal, siginfo_t *info, os_context_t *context)
 {
-#ifdef FOREIGN_FUNCTION_CALL_FLAG
     boolean were_in_lisp;
-#endif
     union interrupt_handler handler;
 
-    check_blockables_blocked_or_lose();
+    check_blockables_blocked_or_lose(0);
 
 #ifndef LISP_FEATURE_WIN32
     if (sigismember(&deferrable_sigset,signal))
         check_interrupts_enabled_or_lose(context);
 #endif
 
-#if defined(LISP_FEATURE_LINUX) || defined(RESTORE_FP_CONTROL_FROM_CONTEXT)
-    /* Under Linux on some architectures, we appear to have to restore
-       the FPU control word from the context, as after the signal is
-       delivered we appear to have a null FPU control word. */
-    os_restore_fp_control(context);
-#endif
-
     handler = interrupt_handlers[signal];
 
     if (ARE_SAME_HANDLER(handler.c, SIG_IGN)) {
         return;
     }
 
-#ifdef FOREIGN_FUNCTION_CALL_FLAG
-    were_in_lisp = !foreign_function_call_active;
+    were_in_lisp = !foreign_function_call_active_p(arch_os_get_current_thread());
     if (were_in_lisp)
-#endif
     {
         fake_foreign_function_call(context);
     }
@@ -883,7 +1021,7 @@ interrupt_handle_now(int signal, siginfo_t *info, os_context_t *context)
         lispobj info_sap, context_sap;
         /* Leave deferrable signals blocked, the handler itself will
          * allow signals again when it sees fit. */
-        unblock_gc_signals();
+        unblock_gc_signals(0, 0);
         context_sap = alloc_sap(context);
         info_sap = alloc_sap(info);
 
@@ -905,9 +1043,7 @@ interrupt_handle_now(int signal, siginfo_t *info, os_context_t *context)
         (*handler.c)(signal, info, context);
     }
 
-#ifdef FOREIGN_FUNCTION_CALL_FLAG
     if (were_in_lisp)
-#endif
     {
         undo_fake_foreign_function_call(context); /* block signals again */
     }
@@ -923,17 +1059,18 @@ interrupt_handle_now(int signal, siginfo_t *info, os_context_t *context)
  * already; we're just doing the Lisp-level processing now that we
  * put off then */
 static void
-run_deferred_handler(struct interrupt_data *data, void *v_context)
+run_deferred_handler(struct interrupt_data *data, os_context_t *context)
 {
     /* The pending_handler may enable interrupts and then another
      * interrupt may hit, overwrite interrupt_data, so reset the
      * pending handler before calling it. Trust the handler to finish
      * with the siginfo before enabling interrupts. */
-    void (*pending_handler) (int, siginfo_t*, void*)=data->pending_handler;
+    void (*pending_handler) (int, siginfo_t*, os_context_t*) =
+        data->pending_handler;
 
     data->pending_handler=0;
     FSHOW_SIGNAL((stderr, "/running deferred handler %p\n", pending_handler));
-    (*pending_handler)(data->pending_signal,&(data->pending_info), v_context);
+    (*pending_handler)(data->pending_signal,&(data->pending_info), context);
 }
 
 #ifndef LISP_FEATURE_WIN32
@@ -943,7 +1080,7 @@ maybe_defer_handler(void *handler, struct interrupt_data *data,
 {
     struct thread *thread=arch_os_get_current_thread();
 
-    check_blockables_blocked_or_lose();
+    check_blockables_blocked_or_lose(0);
 
     if (SymbolValue(INTERRUPT_PENDING,thread) != NIL)
         lose("interrupt already pending\n");
@@ -962,12 +1099,12 @@ maybe_defer_handler(void *handler, struct interrupt_data *data,
      */
     if ((SymbolValue(INTERRUPTS_ENABLED,thread) == NIL) ||
         in_leaving_without_gcing_race_p(thread)) {
-        store_signal_data_for_later(data,handler,signal,info,context);
-        SetSymbolValue(INTERRUPT_PENDING, T,thread);
         FSHOW_SIGNAL((stderr,
                       "/maybe_defer_handler(%x,%d): deferred (RACE=%d)\n",
                       (unsigned int)handler,signal,
                       in_leaving_without_gcing_race_p(thread)));
+        store_signal_data_for_later(data,handler,signal,info,context);
+        SetSymbolValue(INTERRUPT_PENDING, T,thread);
         check_interrupt_context_or_lose(context);
         return 1;
     }
@@ -975,11 +1112,11 @@ maybe_defer_handler(void *handler, struct interrupt_data *data,
      * actually use its argument for anything on x86, so this branch
      * may succeed even when context is null (gencgc alloc()) */
     if (arch_pseudo_atomic_atomic(context)) {
-        store_signal_data_for_later(data,handler,signal,info,context);
-        arch_set_pseudo_atomic_interrupted(context);
         FSHOW_SIGNAL((stderr,
                       "/maybe_defer_handler(%x,%d): deferred(PA)\n",
                       (unsigned int)handler,signal));
+        store_signal_data_for_later(data,handler,signal,info,context);
+        arch_set_pseudo_atomic_interrupted(context);
         check_interrupt_context_or_lose(context);
         return 1;
     }
@@ -1022,16 +1159,12 @@ store_signal_data_for_later (struct interrupt_data *data, void *handler,
 static void
 maybe_now_maybe_later(int signal, siginfo_t *info, void *void_context)
 {
-    os_context_t *context = arch_os_get_context(&void_context);
+    SAVE_ERRNO(signal,context,void_context);
     struct thread *thread = arch_os_get_current_thread();
     struct interrupt_data *data = thread->interrupt_data;
-
-#if defined(LISP_FEATURE_LINUX) || defined(RESTORE_FP_CONTROL_FROM_CONTEXT)
-    os_restore_fp_control(context);
-#endif
-
     if(!maybe_defer_handler(interrupt_handle_now,data,signal,info,context))
         interrupt_handle_now(signal, info, context);
+    RESTORE_ERRNO;
 }
 
 static void
@@ -1039,7 +1172,7 @@ low_level_interrupt_handle_now(int signal, siginfo_t *info,
                                os_context_t *context)
 {
     /* No FP control fixage needed, caller has done that. */
-    check_blockables_blocked_or_lose();
+    check_blockables_blocked_or_lose(0);
     check_interrupts_enabled_or_lose(context);
     (*interrupt_low_level_handlers[signal])(signal, info, context);
     /* No Darwin context fixage needed, caller does that. */
@@ -1048,17 +1181,14 @@ low_level_interrupt_handle_now(int signal, siginfo_t *info,
 static void
 low_level_maybe_now_maybe_later(int signal, siginfo_t *info, void *void_context)
 {
-    os_context_t *context = arch_os_get_context(&void_context);
+    SAVE_ERRNO(signal,context,void_context);
     struct thread *thread = arch_os_get_current_thread();
     struct interrupt_data *data = thread->interrupt_data;
 
-#if defined(LISP_FEATURE_LINUX) || defined(RESTORE_FP_CONTROL_FROM_CONTEXT)
-    os_restore_fp_control(context);
-#endif
-
     if(!maybe_defer_handler(low_level_interrupt_handle_now,data,
                             signal,info,context))
         low_level_interrupt_handle_now(signal, info, context);
+    RESTORE_ERRNO;
 }
 #endif
 
@@ -1066,25 +1196,23 @@ low_level_maybe_now_maybe_later(int signal, siginfo_t *info, void *void_context)
 
 /* This function must not cons, because that may trigger a GC. */
 void
-sig_stop_for_gc_handler(int signal, siginfo_t *info, void *void_context)
+sig_stop_for_gc_handler(int signal, siginfo_t *info, os_context_t *context)
 {
-    os_context_t *context = arch_os_get_context(&void_context);
-
     struct thread *thread=arch_os_get_current_thread();
-    sigset_t ss;
+    boolean was_in_lisp;
 
     /* Test for GC_INHIBIT _first_, else we'd trap on every single
      * pseudo atomic until gc is finally allowed. */
     if (SymbolValue(GC_INHIBIT,thread) != NIL) {
-        SetSymbolValue(STOP_FOR_GC_PENDING,T,thread);
         FSHOW_SIGNAL((stderr, "sig_stop_for_gc deferred (*GC-INHIBIT*)\n"));
+        SetSymbolValue(STOP_FOR_GC_PENDING,T,thread);
         return;
     } else if (arch_pseudo_atomic_atomic(context)) {
+        FSHOW_SIGNAL((stderr,"sig_stop_for_gc deferred (PA)\n"));
         SetSymbolValue(STOP_FOR_GC_PENDING,T,thread);
         arch_set_pseudo_atomic_interrupted(context);
         maybe_save_gc_mask_and_block_deferrables
             (os_context_sigmask_addr(context));
-        FSHOW_SIGNAL((stderr,"sig_stop_for_gc deferred (PA)\n"));
         return;
     }
 
@@ -1092,17 +1220,34 @@ sig_stop_for_gc_handler(int signal, siginfo_t *info, void *void_context)
 
     /* Not PA and GC not inhibited -- we can stop now. */
 
-    /* need the context stored so it can have registers scavenged */
-    fake_foreign_function_call(context);
+    was_in_lisp = !foreign_function_call_active_p(arch_os_get_current_thread());
 
-    /* Block everything. */
-    sigfillset(&ss);
-    thread_sigmask(SIG_BLOCK,&ss,0);
+    if (was_in_lisp) {
+        /* need the context stored so it can have registers scavenged */
+        fake_foreign_function_call(context);
+    }
 
     /* Not pending anymore. */
     SetSymbolValue(GC_PENDING,NIL,thread);
     SetSymbolValue(STOP_FOR_GC_PENDING,NIL,thread);
 
+    /* Consider this: in a PA section GC is requested: GC_PENDING,
+     * pseudo_atomic_interrupted and gc_blocked_deferrables are set,
+     * deferrables are blocked then pseudo_atomic_atomic is cleared,
+     * but a SIG_STOP_FOR_GC arrives before trapping to
+     * interrupt_handle_pending. Here, GC_PENDING is cleared but
+     * pseudo_atomic_interrupted is not and we go on running with
+     * pseudo_atomic_interrupted but without a pending interrupt or
+     * GC. GC_BLOCKED_DEFERRABLES is also left at 1. So let's tidy it
+     * up. */
+    if (thread->interrupt_data->gc_blocked_deferrables) {
+        FSHOW_SIGNAL((stderr,"cleaning up after gc_blocked_deferrables\n"));
+        clear_pseudo_atomic_interrupted(thread);
+        sigcopyset(os_context_sigmask_addr(context),
+                   &thread->interrupt_data->pending_mask);
+        thread->interrupt_data->gc_blocked_deferrables = 0;
+    }
+
     if(thread_state(thread)!=STATE_RUNNING) {
         lose("sig_stop_for_gc_handler: wrong thread state: %ld\n",
              fixnum_value(thread->state));
@@ -1111,6 +1256,12 @@ sig_stop_for_gc_handler(int signal, siginfo_t *info, void *void_context)
     set_thread_state(thread,STATE_SUSPENDED);
     FSHOW_SIGNAL((stderr,"suspended\n"));
 
+    /* While waiting for gc to finish occupy ourselves with zeroing
+     * the unused portion of the control stack to reduce conservatism.
+     * On hypothetic platforms with threads and exact gc it is
+     * actually a must. */
+    scrub_control_stack();
+
     wait_for_thread_state_change(thread, STATE_SUSPENDED);
     FSHOW_SIGNAL((stderr,"resumed\n"));
 
@@ -1119,7 +1270,9 @@ sig_stop_for_gc_handler(int signal, siginfo_t *info, void *void_context)
              fixnum_value(thread_state(thread)));
     }
 
-    undo_fake_foreign_function_call(context);
+    if (was_in_lisp) {
+        undo_fake_foreign_function_call(context);
+    }
 }
 
 #endif
@@ -1127,19 +1280,17 @@ sig_stop_for_gc_handler(int signal, siginfo_t *info, void *void_context)
 void
 interrupt_handle_now_handler(int signal, siginfo_t *info, void *void_context)
 {
-    os_context_t *context = arch_os_get_context(&void_context);
-#if defined(LISP_FEATURE_LINUX) || defined(RESTORE_FP_CONTROL_FROM_CONTEXT)
-    os_restore_fp_control(context);
+    SAVE_ERRNO(signal,context,void_context);
 #ifndef LISP_FEATURE_WIN32
     if ((signal == SIGILL) || (signal == SIGBUS)
 #ifndef LISP_FEATURE_LINUX
         || (signal == SIGEMT)
 #endif
         )
-        corruption_warning_and_maybe_lose("Signal %d recieved", signal);
-#endif
+        corruption_warning_and_maybe_lose("Signal %d received", signal);
 #endif
     interrupt_handle_now(signal, info, context);
+    RESTORE_ERRNO;
 }
 
 /* manipulate the signal context and stack such that when the handler
@@ -1157,8 +1308,10 @@ extern void call_into_lisp_tramp(void);
 void
 arrange_return_to_lisp_function(os_context_t *context, lispobj function)
 {
-    check_gc_signals_unblocked_in_sigset_or_lose
+#ifndef LISP_FEATURE_WIN32
+    check_gc_signals_unblocked_or_lose
         (os_context_sigmask_addr(context));
+#endif
 #if !(defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64))
     void * fun=native_pointer(function);
     void *code = &(((struct simple_fun *) fun)->code);
@@ -1167,8 +1320,14 @@ arrange_return_to_lisp_function(os_context_t *context, lispobj function)
     /* Build a stack frame showing `interrupted' so that the
      * user's backtrace makes (as much) sense (as usual) */
 
-    /* FIXME: what about restoring fp state? */
-    /* FIXME: what about restoring errno? */
+    /* fp state is saved and restored by call_into_lisp */
+    /* FIXME: errno is not restored, but since current uses of this
+     * function only call Lisp code that signals an error, it's not
+     * much of a problem. In other words, running out of the control
+     * stack between a syscall and (GET-ERRNO) may clobber errno if
+     * something fails during signalling or in the handler. But I
+     * can't see what can go wrong as long as there is no CONTINUE
+     * like restart on them. */
 #ifdef LISP_FEATURE_X86
     /* Suppose the existence of some function that saved all
      * registers, called call_into_lisp, then restored GP registers and
@@ -1316,7 +1475,7 @@ arrange_return_to_lisp_function(os_context_t *context, lispobj function)
     *os_context_register_addr(context,reg_LIP) =
         (os_context_register_t)(unsigned long)code;
     *os_context_register_addr(context,reg_CFP) =
-        (os_context_register_t)(unsigned long)current_control_frame_pointer;
+        (os_context_register_t)(unsigned long)access_control_frame_pointer(th);
 #endif
 #ifdef ARCH_HAS_NPC_REGISTER
     *os_context_npc_addr(context) =
@@ -1330,34 +1489,6 @@ arrange_return_to_lisp_function(os_context_t *context, lispobj function)
            (long)function));
 }
 
-#ifdef LISP_FEATURE_SB_THREAD
-
-int
-signal_interrupt_thread(os_thread_t os_thread)
-{
-    /* FSHOW first, in case we are signalling ourselves. */
-    FSHOW((stderr,"/signal_interrupt_thread: %lu\n", os_thread));
-    return kill_safely(os_thread, SIG_INTERRUPT_THREAD);
-}
-
-/* FIXME: this function can go away when all lisp handlers are invoked
- * via arrange_return_to_lisp_function. */
-void
-interrupt_thread_handler(int num, siginfo_t *info, void *v_context)
-{
-    os_context_t *context = (os_context_t*)arch_os_get_context(&v_context);
-
-    FSHOW_SIGNAL((stderr,"/interrupt_thread_handler\n"));
-    check_blockables_blocked_or_lose();
-
-    /* let the handler enable interrupts again when it sees fit */
-    sigaddset_deferrable(os_context_sigmask_addr(context));
-    arrange_return_to_lisp_function(context,
-                                    StaticSymbolFunction(RUN_INTERRUPTION));
-}
-
-#endif
-
 /* KLUDGE: Theoretically the approach we use for undefined alien
  * variables should work for functions as well, but on PPC/Darwin
  * we get bus error at bogus addresses instead, hence this workaround,
@@ -1370,23 +1501,55 @@ undefined_alien_function(void)
     funcall0(StaticSymbolFunction(UNDEFINED_ALIEN_FUNCTION_ERROR));
 }
 
+void lower_thread_control_stack_guard_page(struct thread *th)
+{
+    protect_control_stack_guard_page(0, th);
+    protect_control_stack_return_guard_page(1, th);
+    th->control_stack_guard_page_protected = NIL;
+    fprintf(stderr, "INFO: Control stack guard page unprotected\n");
+}
+
+void reset_thread_control_stack_guard_page(struct thread *th)
+{
+    memset(CONTROL_STACK_GUARD_PAGE(th), 0, os_vm_page_size);
+    protect_control_stack_guard_page(1, th);
+    protect_control_stack_return_guard_page(0, th);
+    th->control_stack_guard_page_protected = T;
+    fprintf(stderr, "INFO: Control stack guard page reprotected\n");
+}
+
+/* Called from the REPL, too. */
+void reset_control_stack_guard_page(void)
+{
+    struct thread *th=arch_os_get_current_thread();
+    if (th->control_stack_guard_page_protected == NIL) {
+        reset_thread_control_stack_guard_page(th);
+    }
+}
+
+void lower_control_stack_guard_page(void)
+{
+    lower_thread_control_stack_guard_page(arch_os_get_current_thread());
+}
+
 boolean
 handle_guard_page_triggered(os_context_t *context,os_vm_address_t addr)
 {
     struct thread *th=arch_os_get_current_thread();
 
-    /* note the os_context hackery here.  When the signal handler returns,
-     * it won't go back to what it was doing ... */
-    if(addr >= CONTROL_STACK_GUARD_PAGE(th) &&
-       addr < CONTROL_STACK_GUARD_PAGE(th) + os_vm_page_size) {
+    if(addr >= CONTROL_STACK_HARD_GUARD_PAGE(th) &&
+       addr < CONTROL_STACK_HARD_GUARD_PAGE(th) + os_vm_page_size) {
+        lose("Control stack exhausted");
+    }
+    else if(addr >= CONTROL_STACK_GUARD_PAGE(th) &&
+            addr < CONTROL_STACK_GUARD_PAGE(th) + os_vm_page_size) {
         /* We hit the end of the control stack: disable guard page
          * protection so the error handler has some headroom, protect the
          * previous page so that we can catch returns from the guard page
          * and restore it. */
-        corruption_warning_and_maybe_lose("Control stack exhausted");
-        protect_control_stack_guard_page(0);
-        protect_control_stack_return_guard_page(1);
-
+        if (th->control_stack_guard_page_protected == NIL)
+            lose("control_stack_guard_page_protected NIL");
+        lower_control_stack_guard_page();
 #ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
         /* For the unfortunate case, when the control stack is
          * exhausted in a signal handler. */
@@ -1402,15 +1565,63 @@ handle_guard_page_triggered(os_context_t *context,os_vm_address_t addr)
          * unprotect this one. This works even if we somehow missed
          * the return-guard-page, and hit it on our way to new
          * exhaustion instead. */
-        fprintf(stderr, "INFO: Control stack guard page reprotected\n");
-        protect_control_stack_guard_page(1);
-        protect_control_stack_return_guard_page(0);
+        if (th->control_stack_guard_page_protected != NIL)
+            lose("control_stack_guard_page_protected not NIL");
+        reset_control_stack_guard_page();
+        return 1;
+    }
+    else if(addr >= BINDING_STACK_HARD_GUARD_PAGE(th) &&
+            addr < BINDING_STACK_HARD_GUARD_PAGE(th) + os_vm_page_size) {
+        lose("Binding stack exhausted");
+    }
+    else if(addr >= BINDING_STACK_GUARD_PAGE(th) &&
+            addr < BINDING_STACK_GUARD_PAGE(th) + os_vm_page_size) {
+        protect_binding_stack_guard_page(0, NULL);
+        protect_binding_stack_return_guard_page(1, NULL);
+        fprintf(stderr, "INFO: Binding stack guard page unprotected\n");
+
+        /* For the unfortunate case, when the binding stack is
+         * exhausted in a signal handler. */
+        unblock_signals_in_context_and_maybe_warn(context);
+        arrange_return_to_lisp_function
+            (context, StaticSymbolFunction(BINDING_STACK_EXHAUSTED_ERROR));
+        return 1;
+    }
+    else if(addr >= BINDING_STACK_RETURN_GUARD_PAGE(th) &&
+            addr < BINDING_STACK_RETURN_GUARD_PAGE(th) + os_vm_page_size) {
+        protect_binding_stack_guard_page(1, NULL);
+        protect_binding_stack_return_guard_page(0, NULL);
+        fprintf(stderr, "INFO: Binding stack guard page reprotected\n");
+        return 1;
+    }
+    else if(addr >= ALIEN_STACK_HARD_GUARD_PAGE(th) &&
+            addr < ALIEN_STACK_HARD_GUARD_PAGE(th) + os_vm_page_size) {
+        lose("Alien stack exhausted");
+    }
+    else if(addr >= ALIEN_STACK_GUARD_PAGE(th) &&
+            addr < ALIEN_STACK_GUARD_PAGE(th) + os_vm_page_size) {
+        protect_alien_stack_guard_page(0, NULL);
+        protect_alien_stack_return_guard_page(1, NULL);
+        fprintf(stderr, "INFO: Alien stack guard page unprotected\n");
+
+        /* For the unfortunate case, when the alien stack is
+         * exhausted in a signal handler. */
+        unblock_signals_in_context_and_maybe_warn(context);
+        arrange_return_to_lisp_function
+            (context, StaticSymbolFunction(ALIEN_STACK_EXHAUSTED_ERROR));
+        return 1;
+    }
+    else if(addr >= ALIEN_STACK_RETURN_GUARD_PAGE(th) &&
+            addr < ALIEN_STACK_RETURN_GUARD_PAGE(th) + os_vm_page_size) {
+        protect_alien_stack_guard_page(1, NULL);
+        protect_alien_stack_return_guard_page(0, NULL);
+        fprintf(stderr, "INFO: Alien stack guard page reprotected\n");
         return 1;
     }
     else if (addr >= undefined_alien_address &&
              addr < undefined_alien_address + os_vm_page_size) {
         arrange_return_to_lisp_function
-          (context, StaticSymbolFunction(UNDEFINED_ALIEN_VARIABLE_ERROR));
+            (context, StaticSymbolFunction(UNDEFINED_ALIEN_VARIABLE_ERROR));
         return 1;
     }
     else return 0;
@@ -1442,10 +1653,9 @@ static volatile int sigaction_nodefer_works = -1;
 static void
 sigaction_nodefer_test_handler(int signal, siginfo_t *info, void *void_context)
 {
-    sigset_t empty, current;
+    sigset_t current;
     int i;
-    sigemptyset(&empty);
-    thread_sigmask(SIG_BLOCK, &empty, &current);
+    get_current_sigmask(&current);
     /* There should be exactly two blocked signals: the two we added
      * to sa_mask when setting up the handler.  NetBSD doesn't block
      * the signal we're handling when SA_NODEFER is set; Linux before
@@ -1489,23 +1699,35 @@ see_if_sigaction_nodefer_works(void)
 static void
 unblock_me_trampoline(int signal, siginfo_t *info, void *void_context)
 {
+    SAVE_ERRNO(signal,context,void_context);
     sigset_t unblock;
 
     sigemptyset(&unblock);
     sigaddset(&unblock, signal);
     thread_sigmask(SIG_UNBLOCK, &unblock, 0);
-    interrupt_handle_now_handler(signal, info, void_context);
+    interrupt_handle_now(signal, info, context);
+    RESTORE_ERRNO;
 }
 
 static void
 low_level_unblock_me_trampoline(int signal, siginfo_t *info, void *void_context)
 {
+    SAVE_ERRNO(signal,context,void_context);
     sigset_t unblock;
 
     sigemptyset(&unblock);
     sigaddset(&unblock, signal);
     thread_sigmask(SIG_UNBLOCK, &unblock, 0);
-    (*interrupt_low_level_handlers[signal])(signal, info, void_context);
+    (*interrupt_low_level_handlers[signal])(signal, info, context);
+    RESTORE_ERRNO;
+}
+
+static void
+low_level_handle_now_handler(int signal, siginfo_t *info, void *void_context)
+{
+    SAVE_ERRNO(signal,context,void_context);
+    (*interrupt_low_level_handlers[signal])(signal, info, context);
+    RESTORE_ERRNO;
 }
 
 void
@@ -1519,30 +1741,20 @@ undoably_install_low_level_interrupt_handler (int signal,
     }
 
     if (ARE_SAME_HANDLER(handler, SIG_DFL))
-        sa.sa_sigaction = handler;
+        sa.sa_sigaction = (void (*)(int, siginfo_t*, void*))handler;
     else if (sigismember(&deferrable_sigset,signal))
         sa.sa_sigaction = low_level_maybe_now_maybe_later;
-    /* The use of a trampoline appears to break the
-       arch_os_get_context() workaround for SPARC/Linux.  For now,
-       don't use the trampoline (and so be vulnerable to the problems
-       that SA_NODEFER is meant to solve. */
-#if !(defined(LISP_FEATURE_SPARC) && defined(LISP_FEATURE_LINUX))
     else if (!sigaction_nodefer_works &&
              !sigismember(&blockable_sigset, signal))
         sa.sa_sigaction = low_level_unblock_me_trampoline;
-#endif
     else
-        sa.sa_sigaction = handler;
+        sa.sa_sigaction = low_level_handle_now_handler;
 
     sigcopyset(&sa.sa_mask, &blockable_sigset);
     sa.sa_flags = SA_SIGINFO | SA_RESTART
         | (sigaction_nodefer_works ? SA_NODEFER : 0);
 #ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
-    if((signal==SIG_MEMORY_FAULT)
-#ifdef SIG_INTERRUPT_THREAD
-       || (signal==SIG_INTERRUPT_THREAD)
-#endif
-       )
+    if((signal==SIG_MEMORY_FAULT))
         sa.sa_flags |= SA_ONSTACK;
 #endif
 
@@ -1554,25 +1766,23 @@ undoably_install_low_level_interrupt_handler (int signal,
 
 /* This is called from Lisp. */
 unsigned long
-install_handler(int signal, void handler(int, siginfo_t*, void*))
+install_handler(int signal, void handler(int, siginfo_t*, os_context_t*))
 {
 #ifndef LISP_FEATURE_WIN32
     struct sigaction sa;
-    sigset_t old, new;
+    sigset_t old;
     union interrupt_handler oldhandler;
 
     FSHOW((stderr, "/entering POSIX install_handler(%d, ..)\n", signal));
 
-    sigemptyset(&new);
-    sigaddset(&new, signal);
-    thread_sigmask(SIG_BLOCK, &new, &old);
+    block_blockable_signals(0, &old);
 
     FSHOW((stderr, "/interrupt_low_level_handlers[signal]=%x\n",
            (unsigned int)interrupt_low_level_handlers[signal]));
     if (interrupt_low_level_handlers[signal]==0) {
         if (ARE_SAME_HANDLER(handler, SIG_DFL) ||
             ARE_SAME_HANDLER(handler, SIG_IGN))
-            sa.sa_sigaction = handler;
+            sa.sa_sigaction = (void (*)(int, siginfo_t*, void*))handler;
         else if (sigismember(&deferrable_sigset, signal))
             sa.sa_sigaction = maybe_now_maybe_later;
         else if (!sigaction_nodefer_works &&
@@ -1604,7 +1814,7 @@ install_handler(int signal, void handler(int, siginfo_t*, void*))
 /* This must not go through lisp as it's allowed anytime, even when on
  * the altstack. */
 void
-sigabrt_handler(int signal, siginfo_t *info, void *void_context)
+sigabrt_handler(int signal, siginfo_t *info, os_context_t *context)
 {
     lose("SIGABRT received.\n");
 }
@@ -1631,7 +1841,7 @@ interrupt_init(void)
              * signal(..)-style one-argument handlers, which is OK
              * because it works to call the 1-argument form where the
              * 3-argument form is expected.) */
-            (void (*)(int, siginfo_t*, void*))SIG_DFL;
+            (void (*)(int, siginfo_t*, os_context_t*))SIG_DFL;
     }
     undoably_install_low_level_interrupt_handler(SIGABRT, sigabrt_handler);
     SHOW("returning from interrupt_init()");
@@ -1656,10 +1866,22 @@ lisp_memory_fault_error(os_context_t *context, os_vm_address_t addr)
     */
     current_memory_fault_address = addr;
     /* To allow debugging memory faults in signal handlers and such. */
-    corruption_warning_and_maybe_lose("Memory fault");
+    corruption_warning_and_maybe_lose("Memory fault at %x (pc=%p, sp=%p)",
+                                      addr,
+                                      *os_context_pc_addr(context),
+#ifdef ARCH_HAS_STACK_POINTER
+                                      *os_context_sp_addr(context)
+#else
+                                      0
+#endif
+                                      );
     unblock_signals_in_context_and_maybe_warn(context);
+#ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
     arrange_return_to_lisp_function(context,
                                     StaticSymbolFunction(MEMORY_FAULT_ERROR));
+#else
+    funcall0(StaticSymbolFunction(MEMORY_FAULT_ERROR));
+#endif
 }
 #endif
 
@@ -1668,7 +1890,7 @@ unhandled_trap_error(os_context_t *context)
 {
     lispobj context_sap;
     fake_foreign_function_call(context);
-    unblock_gc_signals();
+    unblock_gc_signals(0, 0);
     context_sap = alloc_sap(context);
 #ifndef LISP_FEATURE_WIN32
     thread_sigmask(SIG_SETMASK, os_context_sigmask_addr(context), 0);