#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(¤t);
+ sigset = ¤t;
+ }
+ 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);
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
#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(¤t);
- check_deferrables_blocked_in_sigset_or_lose(¤t);
+ 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(¤t);
- for(i = 1; i < NSIG; i++) {
- if (sigismember(&blockable_sigset, i) && !sigismember(¤t, 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(¤t);
- check_gc_signals_unblocked_in_sigset_or_lose(¤t);
+#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)
{
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. */
* 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
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);
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)
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
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
{
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;
/* 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
#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. */
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
* 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;
}
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. */
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)
#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)) {
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);
* 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
{
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");
*/
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;
}
* 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;
}
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
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. */
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
/* 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;
/* 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;
}
/* need the context stored so it can have registers scavenged */
fake_foreign_function_call(context);
- /* Block everything. */
- sigfillset(&ss);
- thread_sigmask(SIG_BLOCK,&ss,0);
-
/* 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));
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"));
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
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);
/* 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
(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,
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. */
* 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;
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, ¤t);
+ get_current_sigmask(¤t);
/* 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
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
}
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
/* 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 &&
/* 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");
}
* 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()");
*/
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
{
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);