*/
#include <stdio.h>
-
+#include <stdlib.h>
+#include <string.h>
#include <signal.h>
-#ifdef mach /* KLUDGE: #ifdef on lowercase symbols? Ick. -- WHN 19990904 */
-#ifdef mips
-#include <mips/cpu.h>
-#endif
-#endif
#include "runtime.h"
#include "arch.h"
#include "alloc.h"
#include "dynbind.h"
#include "interr.h"
+#include "genesis/fdefn.h"
+#include "genesis/simple-fun.h"
void sigaddset_blockable(sigset_t *s)
{
sigaddset(s, SIGPIPE);
sigaddset(s, SIGALRM);
sigaddset(s, SIGURG);
+ sigaddset(s, SIGFPE);
sigaddset(s, SIGTSTP);
sigaddset(s, SIGCHLD);
sigaddset(s, SIGIO);
* becomes 'yes'.) */
boolean internal_errors_enabled = 0;
-os_context_t *lisp_interrupt_contexts[MAX_INTERRUPTS];
+struct interrupt_data * global_interrupt_data;
/* As far as I can tell, what's going on here is:
*
*
* However, some signals need special handling, e.g.
*
- * o the SIGSEGV (for Linux) or SIGBUS (for FreeBSD) used by the
+ * o the SIGSEGV (for e.g. Linux) or SIGBUS (for e.g. FreeBSD) used by the
* garbage collector to detect violations of write protection,
* because some cases of such signals (e.g. GC-related violations of
* write protection) are handled at C level and never passed on to
* - WHN 20000728, dan 20010128 */
-void (*interrupt_low_level_handlers[NSIG]) (int, siginfo_t*, void*) = {0};
-union interrupt_handler interrupt_handlers[NSIG];
-
-/* signal number, siginfo_t, and old mask information for pending signal
- *
- * pending_signal=0 when there is no pending signal. */
-static int pending_signal = 0;
-static siginfo_t pending_info;
-static sigset_t pending_mask;
-
-static boolean maybe_gc_pending = 0;
+boolean maybe_gc_pending = 0;
\f
/*
* utility routines used by various signal handlers
*/
-void
-fake_foreign_function_call(os_context_t *context)
+void
+build_fake_control_stack_frames(struct thread *th,os_context_t *context)
{
- int context_index;
-#ifndef __i386__
+#ifndef LISP_FEATURE_X86
+
lispobj oldcont;
-#endif
- /* Get current Lisp state from context. */
-#ifdef reg_ALLOC
- dynamic_space_free_pointer =
- (lispobj *)(*os_context_register_addr(context, reg_ALLOC));
-#ifdef alpha
- if ((long)dynamic_space_free_pointer & 1) {
- lose("dead in fake_foreign_function_call, context = %x", context);
- }
-#endif
-#endif
-#ifdef reg_BSP
- current_binding_stack_pointer =
- (lispobj *)(*os_context_register_addr(context, reg_BSP));
-#endif
+ /* Build a fake stack frame or frames */
-#ifndef __i386__
- /* Build a fake stack frame. */
current_control_frame_pointer =
(lispobj *)(*os_context_register_addr(context, reg_CSP));
if ((lispobj *)(*os_context_register_addr(context, reg_CFP))
== current_control_frame_pointer) {
/* There is a small window during call where the callee's
* frame isn't built yet. */
- if (LowtagOf(*os_context_register_addr(context, reg_CODE))
- == type_FunctionPointer) {
+ 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] =
oldcont = (lispobj)(*os_context_register_addr(context, reg_OCFP));
}
}
- /* ### We can't tell whether we are still in the caller if it had
- * to reg_ALLOCate the stack frame due to stack arguments. */
- /* ### Can anything strange happen during return? */
+ /* We can't tell whether we are still in the caller if it had to
+ * allocate a stack frame due to stack arguments. */
+ /* This observation provoked some past CMUCL maintainer to ask
+ * "Can anything strange happen during return?" */
else {
/* normal case */
oldcont = (lispobj)(*os_context_register_addr(context, reg_CFP));
current_control_frame_pointer[2] =
(lispobj)(*os_context_register_addr(context, reg_CODE));
#endif
+}
+
+void
+fake_foreign_function_call(os_context_t *context)
+{
+ int context_index;
+ struct thread *thread=arch_os_get_current_thread();
+
+ /* Get current Lisp state from context. */
+#ifdef reg_ALLOC
+ dynamic_space_free_pointer =
+ (lispobj *)(*os_context_register_addr(context, reg_ALLOC));
+#ifdef alpha
+ if ((long)dynamic_space_free_pointer & 1) {
+ lose("dead in fake_foreign_function_call, context = %x", context);
+ }
+#endif
+#endif
+#ifdef reg_BSP
+ current_binding_stack_pointer =
+ (lispobj *)(*os_context_register_addr(context, reg_BSP));
+#endif
+
+ build_fake_control_stack_frames(thread,context);
/* Do dynamic binding of the active interrupt context index
* and save the context in the context array. */
- context_index = SymbolValue(FREE_INTERRUPT_CONTEXT_INDEX)>>2;
- /* FIXME: Ick! Why use abstract "make_fixnum" in some places if
- * you're going to convert from fixnum by bare >>2 in other
- * places? Use fixnum_value(..) here, and look for other places
- * which do bare >> and << for fixnum_value and make_fixnum. */
-
+ context_index =
+ fixnum_value(SymbolValue(FREE_INTERRUPT_CONTEXT_INDEX,thread));
+
if (context_index >= MAX_INTERRUPTS) {
- lose("maximum interrupt nesting depth (%d) exceeded",
- MAX_INTERRUPTS);
+ lose("maximum interrupt nesting depth (%d) exceeded", MAX_INTERRUPTS);
}
bind_variable(FREE_INTERRUPT_CONTEXT_INDEX,
- make_fixnum(context_index + 1));
+ make_fixnum(context_index + 1),thread);
- lisp_interrupt_contexts[context_index] = context;
+ thread->interrupt_contexts[context_index] = context;
/* no longer in Lisp now */
foreign_function_call_active = 1;
void
undo_fake_foreign_function_call(os_context_t *context)
{
+ struct thread *thread=arch_os_get_current_thread();
/* Block all blockable signals. */
sigset_t block;
sigemptyset(&block);
* FREE_INTERRUPT_CONTEXT_INDEX? If so, we should say so. And
* perhaps yes, unbind_to_here() really would be clearer and less
* fragile.. */
- unbind();
+ /* dan (2001.08.10) thinks the above supposition is probably correct */
+ unbind(thread);
#ifdef reg_ALLOC
/* Put the dynamic space free pointer back into the context. */
void
interrupt_handle_pending(os_context_t *context)
{
+ struct thread *thread;
+ struct interrupt_data *data;
+
#ifndef __i386__
boolean were_in_lisp = !foreign_function_call_active;
#endif
-
- SetSymbolValue(INTERRUPT_PENDING, NIL);
+#ifdef LISP_FEATURE_SB_THREAD
+ while(stop_the_world) kill(getpid(),SIGSTOP);
+#endif
+ thread=arch_os_get_current_thread();
+ data=thread->interrupt_data;
+ SetSymbolValue(INTERRUPT_PENDING, NIL,thread);
if (maybe_gc_pending) {
- maybe_gc_pending = 0;
#ifndef __i386__
if (were_in_lisp)
#endif
{
fake_foreign_function_call(context);
}
- funcall0(SymbolFunction(MAYBE_GC));
+ funcall0(SymbolFunction(SUB_GC));
#ifndef __i386__
if (were_in_lisp)
#endif
* anyway. Why we still need to copy the pending_mask into the
* context given that we're now done with the context anyway, I
* couldn't say. */
- memcpy(os_context_sigmask_addr(context), &pending_mask, sizeof(sigset_t));
- sigemptyset(&pending_mask);
- if (pending_signal) {
- int signal = pending_signal;
+#if 0
+ memcpy(os_context_sigmask_addr(context), &pending_mask,
+ 4 /* sizeof(sigset_t) */ );
+#endif
+ sigemptyset(&data->pending_mask);
+ if (data->pending_signal) {
+ int signal = data->pending_signal;
siginfo_t info;
- memcpy(&info, &pending_info, sizeof(siginfo_t));
- pending_signal = 0;
+ memcpy(&info, &data->pending_info, sizeof(siginfo_t));
+ data->pending_signal = 0;
interrupt_handle_now(signal, &info, context);
}
}
* 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, void *void_context)
{
os_context_t *context = (os_context_t*)void_context;
+ struct thread *thread=arch_os_get_current_thread();
#ifndef __i386__
boolean were_in_lisp;
#endif
union interrupt_handler handler;
- /* FIXME: The CMU CL we forked off of had this Linux-only
- * operation here. Newer CMU CLs (e.g. 18c) have hairier
- * Linux/i386-only logic here. SBCL seems to be more reliable
- * without anything here. However, if we start supporting code
- * which sets the rounding mode, then we may want to do something
- * special to force the rounding mode back to some standard value
- * here, so that ISRs can have a standard environment. (OTOH, if
- * rounding modes are under user control, then perhaps we should
- * leave this up to the user.)
- *
- * In the absence of a test case to show that this is really a
- * problem, we just suppress this code completely (just like the
- * parallel code in maybe_now_maybe_later).
- * #ifdef __linux__
- * SET_FPU_CONTROL_WORD(context->__fpregs_mem.cw);
- * #endif */
-
- handler = interrupt_handlers[signal];
+#ifdef LISP_FEATURE_LINUX
+ /* 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 = thread->interrupt_data->interrupt_handlers[signal];
if (ARE_SAME_HANDLER(handler.c, SIG_IGN)) {
return;
}
-
+
#ifndef __i386__
were_in_lisp = !foreign_function_call_active;
if (were_in_lisp)
* support decides to pass on it. */
lose("no handler for signal %d in interrupt_handle_now(..)", signal);
- } else if (LowtagOf(handler.lisp) == type_FunctionPointer) {
+ } else if (lowtag_of(handler.lisp) == FUN_POINTER_LOWTAG) {
/* Allocate the SAPs while the interrupts are still disabled.
* (FIXME: Why? This is the way it was done in CMU CL, and it
}
static void
-maybe_now_maybe_later(int signal, siginfo_t *info, void *void_context)
+store_signal_data_for_later (struct interrupt_data *data, int signal,
+ siginfo_t *info, os_context_t *context)
{
- os_context_t *context = (os_context_t*)void_context;
+ data->pending_signal = signal;
+ memcpy(&(data->pending_info), info, sizeof(siginfo_t));
+ memcpy(&(data->pending_mask),
+ os_context_sigmask_addr(context),
+ sizeof(sigset_t));
+ sigaddset_blockable(os_context_sigmask_addr(context));
+}
- /* FIXME: See Debian cmucl 2.4.17, and mail from DTC on the CMU CL
- * mailing list 23 Oct 1999, for changes in FPU handling at
- * interrupt time which should be ported into SBCL. Also see the
- * analogous logic at the head of interrupt_handle_now for
- * more related FIXME stuff.
- *
- * For now, we just suppress this code completely.
- * #ifdef __linux__
- * SET_FPU_CONTROL_WORD(context->__fpregs_mem.cw);
- * #endif */
+static void
+maybe_now_maybe_later(int signal, siginfo_t *info, void *void_context)
+{
+ os_context_t *context = arch_os_get_context(&void_context);
+ struct thread *thread=arch_os_get_current_thread();
+ struct interrupt_data *data=thread->interrupt_data;
+#ifdef LISP_FEATURE_LINUX
+ os_restore_fp_control(context);
+#endif
/* see comments at top of code/signal.lisp for what's going on here
* with INTERRUPTS_ENABLED/INTERRUPT_HANDLE_NOW
*/
- if (SymbolValue(INTERRUPTS_ENABLED) == NIL) {
-
- /* FIXME: This code is exactly the same as the code in the
- * other leg of the if(..), and should be factored out into
- * a shared function. */
- pending_signal = signal;
- memcpy(&pending_info, info, sizeof(siginfo_t));
- memcpy(&pending_mask,
- os_context_sigmask_addr(context),
- sizeof(sigset_t));
- sigaddset_blockable(os_context_sigmask_addr(context));
- SetSymbolValue(INTERRUPT_PENDING, T);
-
+ if (SymbolValue(INTERRUPTS_ENABLED,thread) == NIL) {
+ store_signal_data_for_later(data,signal,info,context);
+ SetSymbolValue(INTERRUPT_PENDING, T,thread);
} else if (
#ifndef __i386__
(!foreign_function_call_active) &&
#endif
arch_pseudo_atomic_atomic(context)) {
-
- /* FIXME: It would probably be good to replace these bare
- * memcpy(..) calls with calls to cpy_siginfo_t and
- * cpy_sigset_t, so that we only have to get the sizeof
- * expressions right in one place, and after that static type
- * checking takes over. */
- pending_signal = signal;
- memcpy(&pending_info, info, sizeof(siginfo_t));
- memcpy(&pending_mask,
- os_context_sigmask_addr(context),
- sizeof(sigset_t));
- sigaddset_blockable(os_context_sigmask_addr(context));
-
+ store_signal_data_for_later(data,signal,info,context);
arch_set_pseudo_atomic_interrupted(context);
-
} else {
interrupt_handle_now(signal, info, context);
}
}
\f
+
+void
+interrupt_handle_now_handler(int signal, siginfo_t *info, void *void_context)
+{
+ os_context_t *context = arch_os_get_context(&void_context);
+ interrupt_handle_now(signal, info, context);
+}
+
/*
* stuff to detect and handle hitting the GC trigger
*/
-#ifndef INTERNAL_GC_TRIGGER
+#ifndef LISP_FEATURE_GENCGC
+/* since GENCGC has its own way to record trigger */
static boolean
gc_trigger_hit(int signal, siginfo_t *info, os_context_t *context)
{
if (current_auto_gc_trigger == NULL)
return 0;
else{
- lispobj *badaddr=(lispobj *)arch_get_bad_addr(signal,
- info,
- context);
-
- return (badaddr >= current_auto_gc_trigger &&
- badaddr < current_dynamic_space + DYNAMIC_SPACE_SIZE);
+ void *badaddr=arch_get_bad_addr(signal,info,context);
+ return (badaddr >= (void *)current_auto_gc_trigger &&
+ badaddr <((void *)current_dynamic_space + DYNAMIC_SPACE_SIZE));
}
}
#endif
-#ifndef __i386__
-/* This function gets called from the SIGSEGV (Linux) or SIGBUS (BSD)
- * handler. Here we check whether the signal was due to treading on
- * the mprotect()ed zone - and if so, arrange for a GC to happen.
- */
+/* and similarly for the control stack guard page */
+
+boolean handle_control_stack_guard_triggered(os_context_t *context,void *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>=(void *)CONTROL_STACK_GUARD_PAGE(th) &&
+ addr<(void *)(CONTROL_STACK_GUARD_PAGE(th)+os_vm_page_size)) {
+ void *fun;
+ void *code;
+ /* fprintf(stderr, "hit end of control stack\n"); */
+ /* we hit the end of the control stack. disable protection
+ * temporarily so the error handler has some headroom */
+ protect_control_stack_guard_page(th->pid,0L);
+
+ fun = (void *)
+ native_pointer((lispobj) SymbolFunction(CONTROL_STACK_EXHAUSTED_ERROR));
+ code = &(((struct simple_fun *) fun)->code);
+
+ /* Build a stack frame showing `interrupted' so that the
+ * user's backtrace makes (as much) sense (as usual) */
+ build_fake_control_stack_frames(th,context);
+ /* signal handler will "return" to this error-causing function */
+ *os_context_pc_addr(context) = code;
+#ifdef LISP_FEATURE_X86
+ *os_context_register_addr(context,reg_ECX) = 0;
+#else
+ /* this much of the calling convention is common to all
+ non-x86 ports */
+ *os_context_register_addr(context,reg_NARGS) = 0;
+ *os_context_register_addr(context,reg_LIP) = code;
+ *os_context_register_addr(context,reg_CFP) =
+ current_control_frame_pointer;
+#endif
+#ifdef ARCH_HAS_NPC_REGISTER
+ *os_context_npc_addr(context) =
+ 4 + *os_context_pc_addr(context);
+#endif
+#ifdef LISP_FEATURE_SPARC
+ /* Bletch. This is a feature of the SPARC calling convention,
+ which sadly I'm not going to go into in large detail here,
+ as I don't know it well enough. Suffice to say that if the
+ line
+
+ (INST MOVE CODE-TN FUNCTION)
+
+ in compiler/sparc/call.lisp is changed, then this bit can
+ probably go away. -- CSR, 2002-07-24 */
+ *os_context_register_addr(context,reg_CODE) =
+ fun + FUN_POINTER_LOWTAG;
+#endif
+ return 1;
+ }
+ else return 0;
+}
+
+#ifndef LISP_FEATURE_GENCGC
+/* This function gets called from the SIGSEGV (for e.g. Linux or
+ * OpenBSD) or SIGBUS (for e.g. FreeBSD) handler. Here we check
+ * whether the signal was due to treading on the mprotect()ed zone -
+ * and if so, arrange for a GC to happen. */
+extern unsigned long bytes_consed_between_gcs; /* gc-common.c */
+
boolean
interrupt_maybe_gc(int signal, siginfo_t *info, void *void_context)
{
os_context_t *context=(os_context_t *) void_context;
+ struct thread *th=arch_os_get_current_thread();
+ struct interrupt_data *data=
+ th ? th->interrupt_data : global_interrupt_data;
- if (!foreign_function_call_active
-#ifndef INTERNAL_GC_TRIGGER
- && gc_trigger_hit(signal, info, context)
-#endif
- ) {
-#ifndef INTERNAL_GC_TRIGGER
+ if(!foreign_function_call_active && gc_trigger_hit(signal, info, context)){
clear_auto_gc_trigger();
-#endif
if (arch_pseudo_atomic_atomic(context)) {
/* don't GC during an atomic operation. Instead, copy the
* will detect pending_signal==0 and know to do a GC with the
* signal context instead of calling a Lisp-level handler */
maybe_gc_pending = 1;
- if (pending_signal == 0) {
+ if (data->pending_signal == 0) {
/* FIXME: This copy-pending_mask-then-sigaddset_blockable
* idiom occurs over and over. It should be factored out
* into a function with a descriptive name. */
- memcpy(&pending_mask,
+ memcpy(&(data->pending_mask),
os_context_sigmask_addr(context),
sizeof(sigset_t));
sigaddset_blockable(os_context_sigmask_addr(context));
}
else {
fake_foreign_function_call(context);
- funcall0(SymbolFunction(MAYBE_GC));
+ /* SUB-GC may return without GCing if *GC-INHIBIT* is set,
+ * in which case we will be running with no gc trigger
+ * barrier thing for a while. But it shouldn't be long
+ * until the end of WITHOUT-GCING. */
+ funcall0(SymbolFunction(SUB_GC));
undo_fake_foreign_function_call(context);
- }
-
+ }
return 1;
} else {
return 0;
* noise to install handlers
*/
-/* Install a special low-level handler for signal; or if handler is
- * SIG_DFL, remove any special handling for signal. */
+/* SBCL used to have code to restore signal handlers on exit, which
+ * has been removed from the threaded version until we decide: exit of
+ * _what_ ? */
+
+/* SBCL comment: The "undoably" aspect is because we also arrange with
+ * atexit() for the handler to be restored to its old value. This is
+ * for tidiness: it shouldn't matter much ordinarily, but it does
+ * remove a window where e.g. memory fault signals (SIGSEGV or SIGBUS,
+ * which in ordinary operation of SBCL are sent to the generational
+ * garbage collector, then possibly onward to Lisp code) or SIGINT
+ * (which is ordinarily passed to Lisp code) could otherwise be
+ * handled bizarrely/brokenly because the Lisp code would try to deal
+ * with them using machinery (like stream output buffers) which has
+ * already been dismantled. */
+
+/* I'm not sure (a) whether this is a real concern, (b) how it helps
+ anyway */
+
void
-interrupt_install_low_level_handler (int signal,
- void handler(int, siginfo_t*, void*))
+uninstall_low_level_interrupt_handlers_atexit(void)
+{
+}
+
+void
+undoably_install_low_level_interrupt_handler (int signal,
+ void handler(int,
+ siginfo_t*,
+ void*))
{
struct sigaction sa;
+ struct thread *th=arch_os_get_current_thread();
+ struct interrupt_data *data=
+ th ? th->interrupt_data : global_interrupt_data;
+
+ if (0 > signal || signal >= NSIG) {
+ lose("bad signal number %d", signal);
+ }
sa.sa_sigaction = handler;
sigemptyset(&sa.sa_mask);
sigaddset_blockable(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO | SA_RESTART;
-
+#ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
+ if(signal==SIG_MEMORY_FAULT) sa.sa_flags|= SA_ONSTACK;
+#endif
+
sigaction(signal, &sa, NULL);
- interrupt_low_level_handlers[signal] =
- (ARE_SAME_HANDLER(handler,SIG_DFL) ? 0 : handler);
+ data->interrupt_low_level_handlers[signal] =
+ (ARE_SAME_HANDLER(handler, SIG_DFL) ? 0 : handler);
}
/* This is called from Lisp. */
struct sigaction sa;
sigset_t old, new;
union interrupt_handler oldhandler;
+ struct thread *th=arch_os_get_current_thread();
+ struct interrupt_data *data=
+ th ? th->interrupt_data : global_interrupt_data;
FSHOW((stderr, "/entering POSIX install_handler(%d, ..)\n", signal));
FSHOW((stderr, "/interrupt_low_level_handlers[signal]=%d\n",
interrupt_low_level_handlers[signal]));
- if (interrupt_low_level_handlers[signal]==0) {
+ if (data->interrupt_low_level_handlers[signal]==0) {
if (ARE_SAME_HANDLER(handler, SIG_DFL) ||
ARE_SAME_HANDLER(handler, SIG_IGN)) {
sa.sa_sigaction = handler;
} else if (sigismember(&new, signal)) {
sa.sa_sigaction = maybe_now_maybe_later;
} else {
- sa.sa_sigaction = interrupt_handle_now;
+ sa.sa_sigaction = interrupt_handle_now_handler;
}
sigemptyset(&sa.sa_mask);
sigaddset_blockable(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO | SA_RESTART;
-
sigaction(signal, &sa, NULL);
}
- oldhandler = interrupt_handlers[signal];
- interrupt_handlers[signal].c = handler;
+ oldhandler = data->interrupt_handlers[signal];
+ data->interrupt_handlers[signal].c = handler;
sigprocmask(SIG_SETMASK, &old, 0);
}
void
-interrupt_init(void)
+interrupt_init()
{
int i;
-
SHOW("entering interrupt_init()");
+ global_interrupt_data=calloc(sizeof(struct interrupt_data), 1);
+
+ /* Set up high level handler information. */
for (i = 0; i < NSIG; i++) {
- interrupt_handlers[i].c =
+ global_interrupt_data->interrupt_handlers[i].c =
/* (The cast here blasts away the distinction between
* SA_SIGACTION-style three-argument handlers and
* signal(..)-style one-argument handlers, which is OK
* 3-argument form is expected.) */
(void (*)(int, siginfo_t*, void*))SIG_DFL;
}
+
SHOW("returning from interrupt_init()");
}