Update of /cvsroot/sbcl/sbcl/src/runtime In directory sc8-pr-cvs1:/tmp/cvs-serv3708/src/runtime Modified Files: Tag: stop_the_world_branch gencgc.c interrupt.c interrupt.h linux-os.c linux-os.h thread.c x86-arch.h x86-linux-os.c Log Message: 0.8.2.38.stop_the_world.2 (let ((n (copy-seq "courtney"))) (setf (subseq n 2 6) "stop") n) [ cookie for the first person to get the song reference ] Part Two of the signals refactoring spree introduces new functions maybe_defer_handler and run_deferred_handler, which encapsulate a lot of the "is it safe to run this handler now? no, ok, copy its siginfo somewhere safe and do it later" cruft that's presently done ad hoc. Compiles. Untested. Still unlikely to work. Next up: gc.lisp Index: gencgc.c =================================================================== RCS file: /cvsroot/sbcl/sbcl/src/runtime/gencgc.c,v retrieving revision 1.35 retrieving revision 1.35.2.1 diff -u -d -r1.35 -r1.35.2.1 --- gencgc.c 5 Aug 2003 14:11:40 -0000 1.35 +++ gencgc.c 18 Aug 2003 11:06:19 -0000 1.35.2.1 @@ -4204,7 +4204,7 @@ __asm__("movl %fs,%0" : "=r" (fs) : ); fprintf(stderr, "fs is %x, th->tls_cookie=%x (should be identical)\n", debug_get_fs(),th->tls_cookie); - lose("If you see this message before 2003.05.01, mail details to sbcl-devel\n"); + lose("If you see this message before 2003.12.01, mail details to sbcl-devel\n"); } #else gc_assert(SymbolValue(PSEUDO_ATOMIC_ATOMIC,th)); Index: interrupt.c =================================================================== RCS file: /cvsroot/sbcl/sbcl/src/runtime/interrupt.c,v retrieving revision 1.41 retrieving revision 1.41.2.1 diff -u -d -r1.41 -r1.41.2.1 --- interrupt.c 17 Aug 2003 15:29:36 -0000 1.41 +++ interrupt.c 18 Aug 2003 11:06:19 -0000 1.41.2.1 @@ -13,6 +13,34 @@ * files for more information. */ + +/* As far as I can tell, what's going on here is: + * + * In the case of most signals, when Lisp asks us to handle the + * signal, the outermost handler (the one actually passed to UNIX) is + * either interrupt_handle_now(..) or maybe_now_maybe_later(..). + * In that case, the Lisp-level handler is stored in interrupt_handlers[..] + * and interrupt_low_level_handlers[..] is cleared. + * + * However, some signals need special handling, e.g. + * + * 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 + * Lisp. For such signals, we still store any Lisp-level handler + * in interrupt_handlers[..], but for the outermost handle we use + * the value from interrupt_low_level_handlers[..], instead of the + * ordinary interrupt_handle_now(..) or interrupt_handle_later(..). + * + * o the SIGTRAP (Linux/Alpha) which Lisp code uses to handle breakpoints, + * pseudo-atomic sections, and some classes of error (e.g. "function + * not defined"). This never goes anywhere near the Lisp handlers at all. + * See runtime/alpha-arch.c and code/signal.lisp + * + * - WHN 20000728, dan 20010128 */ + + #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -34,6 +62,14 @@ #include "genesis/fdefn.h" #include "genesis/simple-fun.h" +void run_deferred_handler(struct interrupt_data *data, void *v_context) ; +static void store_signal_data_for_later (struct interrupt_data *data, + void *handler, int signal, + siginfo_t *info, + os_context_t *context); +extern lispobj all_threads_lock; +extern int countdown_to_gc; + void sigaddset_blockable(sigset_t *s) { sigaddset(s, SIGHUP); @@ -53,6 +89,10 @@ sigaddset(s, SIGWINCH); sigaddset(s, SIGUSR1); sigaddset(s, SIGUSR2); +#ifdef LISP_FEATURE_SB_THREAD + sigaddset(s, SIG_STOP_FOR_GC); + sigaddset(s, SIG_INTERRUPT_THREAD); +#endif } /* When we catch an internal error, should we pass it back to Lisp to @@ -64,33 +104,6 @@ struct interrupt_data * global_interrupt_data; -/* As far as I can tell, what's going on here is: - * - * In the case of most signals, when Lisp asks us to handle the - * signal, the outermost handler (the one actually passed to UNIX) is - * either interrupt_handle_now(..) or maybe_now_maybe_later(..). - * In that case, the Lisp-level handler is stored in interrupt_handlers[..] - * and interrupt_low_level_handlers[..] is cleared. - * - * However, some signals need special handling, e.g. - * - * 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 - * Lisp. For such signals, we still store any Lisp-level handler - * in interrupt_handlers[..], but for the outermost handle we use - * the value from interrupt_low_level_handlers[..], instead of the - * ordinary interrupt_handle_now(..) or interrupt_handle_later(..). - * - * o the SIGTRAP (Linux/Alpha) which Lisp code uses to handle breakpoints, - * pseudo-atomic sections, and some classes of error (e.g. "function - * not defined"). This never goes anywhere near the Lisp handlers at all. - * See runtime/alpha-arch.c and code/signal.lisp - * - * - WHN 20000728, dan 20010128 */ - - boolean maybe_gc_pending = 0; /* @@ -202,13 +215,7 @@ /* going back into Lisp */ foreign_function_call_active = 0; - /* Undo dynamic binding. */ - /* ### Do I really need to unbind_to_here()? */ - /* FIXME: Is this to undo the binding of - * FREE_INTERRUPT_CONTEXT_INDEX? If so, we should say so. And - * perhaps yes, unbind_to_here() really would be clearer and less - * fragile.. */ - /* dan (2001.08.10) thinks the above supposition is probably correct */ + /* Undo dynamic binding of FREE_INTERRUPT_CONTEXT_INDEX */ unbind(thread); #ifdef reg_ALLOC @@ -258,81 +265,22 @@ } } -/* This function handles pending interrupts. Note that in C/kernel - * terms we dealt with the signal already; we just haven't decided - * whether to call a Lisp handler or do a GC or something like that. - * If it helps, you can think of pending_{signal,mask,info} as a - * one-element queue of signals that we have acknowledged but not - * processed */ - 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 -#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) { -#ifndef __i386__ - if (were_in_lisp) -#endif - { - fake_foreign_function_call(context); - } - funcall0(SymbolFunction(SUB_GC)); -#ifndef __i386__ - if (were_in_lisp) -#endif - { - undo_fake_foreign_function_call(context); - } - } - - /* FIXME: This isn't very clear. It would be good to reverse - * engineer it and rewrite the code more clearly, or write a clear - * explanation of what's going on in the comments, or both. - * - * WHN's question 1a: How come we unconditionally copy from - * pending_mask into the context, and then test whether - * pending_signal is set? - * - * WHN's question 1b: If pending_signal wasn't set, how could - * pending_mask be valid? - * - * Dan Barlow's reply (sbcl-devel 2001-03-13): And the answer is - - * or appears to be - because interrupt_maybe_gc set it that way - * (look in the #ifndef __i386__ bit). We can't GC during a - * pseudo-atomic, so we set maybe_gc_pending=1 and - * arch_set_pseudo_atomic_interrupted(..) When we come out of - * pseudo_atomic we're marked as interrupted, so we call - * interrupt_handle_pending, which does the GC using the pending - * context (it needs a context so that it has registers to use as - * GC roots) then notices there's no actual interrupt handler to - * call, so doesn't. That's the second question [1b] answered, - * 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. */ #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, &data->pending_info, sizeof(siginfo_t)); - data->pending_signal = 0; - interrupt_handle_now(signal, &info, context); - } + run_deferred_handler(data,(void *)context); /* XXX */ } /* @@ -394,6 +342,10 @@ lose("no handler for signal %d in interrupt_handle_now(..)", signal); } else if (lowtag_of(handler.lisp) == FUN_POINTER_LOWTAG) { + /* Once we've decided what to do about contexts in a + * return-elsewhere world (the original context will no longer + * be available; should we copy it or was nobody using it anyway?) + * then we should convert this to return-elsewhere */ /* Allocate the SAPs while the interrupts are still disabled. * (FIXME: Why? This is the way it was done in CMU CL, and it @@ -438,10 +390,45 @@ #endif } +/* This is called at the end of a critical section if the indications + * are that some signal was deferred during the section. Note that as + * far as C or the kernel is concerned we dealt with the signal + * already; we're just doing the Lisp-level processing now that we + * put off then */ + +void +run_deferred_handler(struct interrupt_data *data, void *v_context) { + (*(data->pending_handler)) + (data->pending_signal,&(data->pending_info), v_context); +} + +boolean +maybe_defer_handler(void *handler, struct interrupt_data *data, + int signal, siginfo_t *info, os_context_t *context) +{ + struct thread *thread=arch_os_get_current_thread(); + if (SymbolValue(INTERRUPTS_ENABLED,thread) == NIL) { + store_signal_data_for_later(data,handler,signal,info,context); + SetSymbolValue(INTERRUPT_PENDING, T,thread); + return 1; + } + if ( +#ifndef __i386__ + (!foreign_function_call_active) && +#endif + arch_pseudo_atomic_atomic(context)) { + store_signal_data_for_later(data,handler,signal,info,context); + arch_set_pseudo_atomic_interrupted(context); + return 1; + } + return 0; +} static void -store_signal_data_for_later (struct interrupt_data *data, int signal, +store_signal_data_for_later (struct interrupt_data *data, void *handler, + int signal, siginfo_t *info, os_context_t *context) { + data->pending_handler = handler; data->pending_signal = signal; memcpy(&(data->pending_info), info, sizeof(siginfo_t)); memcpy(&(data->pending_mask), @@ -460,24 +447,32 @@ #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,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)) { - store_signal_data_for_later(data,signal,info,context); - arch_set_pseudo_atomic_interrupted(context); - } else { - interrupt_handle_now(signal, info, context); - } + if(maybe_defer_handler(interrupt_handle_now,data, + signal,info,context)) + return; + interrupt_handle_now(signal, info, context); +} + +void +sig_stop_for_gc_handler(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; + sigset_t block; + + if(maybe_defer_handler(sig_stop_for_gc_handler,data, + signal,info,context)) + return; + + sigemptyset(&block); + sigaddset_blockable(&block); + sigprocmask(SIG_BLOCK, &block, 0); + get_spinlock(&all_threads_lock,thread->pid); + countdown_to_gc--; + release_spinlock(&all_threads_lock); + kill(getpid(),SIGSTOP); } - void interrupt_handle_now_handler(int signal, siginfo_t *info, void *void_context) @@ -575,16 +570,19 @@ } #ifdef LISP_FEATURE_SB_THREAD -boolean handle_rt_signal(int num, siginfo_t *info, void *v_context) +void handle_rt_signal(int num, siginfo_t *info, void *v_context) { - struct - os_context_t *context = (os_context_t*)arch_os_get_context(&v_context); + os_context_t *context = (os_context_t*)arch_os_get_context(&v_context); + struct thread *th=arch_os_get_current_thread(); + struct interrupt_data *data= + th ? th->interrupt_data : global_interrupt_data; + if(maybe_defer_handler(handle_rt_signal,data,num,info,context)) + return ; arrange_return_to_lisp_function(context,info->si_value.sival_int); } #endif -boolean handle_control_stack_guard_triggered(os_context_t *context,void *addr) -{ +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 ... */ @@ -618,66 +616,32 @@ if(!foreign_function_call_active && gc_trigger_hit(signal, info, context)){ clear_auto_gc_trigger(); - - if (arch_pseudo_atomic_atomic(context)) { - /* don't GC during an atomic operation. Instead, copy the - * signal mask somewhere safe. interrupt_handle_pending - * 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 (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(&(data->pending_mask), - os_context_sigmask_addr(context), - sizeof(sigset_t)); - sigaddset_blockable(os_context_sigmask_addr(context)); - } - arch_set_pseudo_atomic_interrupted(context); - } - else { - fake_foreign_function_call(context); - /* 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); - } + if(!maybe_defer_handler + (interrupt_maybe_gc_int,data,signal,info,void_context)) + interrupt_maybe_gc_int(signal,info,void_context); return 1; - } else { - return 0; } + return 0; +} + +boolean +interrupt_maybe_gc_int(int signal, siginfo_t *info, void *void_context) +{ + os_context_t *context=(os_context_t *) void_context; + fake_foreign_function_call(context); + /* 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; } #endif /* * noise to install handlers */ - -/* 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 -uninstall_low_level_interrupt_handlers_atexit(void) -{ -} void undoably_install_low_level_interrupt_handler (int signal, Index: interrupt.h =================================================================== RCS file: /cvsroot/sbcl/sbcl/src/runtime/interrupt.h,v retrieving revision 1.8 retrieving revision 1.8.2.1 diff -u -d -r1.8 -r1.8.2.1 --- interrupt.h 16 Aug 2003 20:38:40 -0000 1.8 +++ interrupt.h 18 Aug 2003 11:06:19 -0000 1.8.2.1 @@ -32,8 +32,9 @@ void (*interrupt_low_level_handlers[NSIG]) (int, siginfo_t*, void*) ; 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. */ + /* signal information for pending signal. pending_signal=0 when there + * is no pending signal. */ + void (*pending_handler) (int, siginfo_t*, void*) ; int pending_signal ; siginfo_t pending_info; sigset_t pending_mask; @@ -49,7 +50,8 @@ boolean continuable); extern boolean handle_control_stack_guard_triggered(os_context_t *,void *); extern boolean interrupt_maybe_gc(int, siginfo_t*, void*); -extern boolean handle_rt_signal(int, siginfo_t*, void*); +extern void handle_rt_signal(int, siginfo_t*, void*); +extern void sig_stop_for_gc_handler(int, siginfo_t*, void*); extern void undoably_install_low_level_interrupt_handler (int signal, void handler(int, Index: linux-os.c =================================================================== RCS file: /cvsroot/sbcl/sbcl/src/runtime/linux-os.c,v retrieving revision 1.25 retrieving revision 1.25.2.1 diff -u -d -r1.25 -r1.25.2.1 --- linux-os.c 16 Aug 2003 20:38:40 -0000 1.25 +++ linux-os.c 18 Aug 2003 11:06:19 -0000 1.25.2.1 @@ -260,8 +260,12 @@ { undoably_install_low_level_interrupt_handler(SIG_MEMORY_FAULT, sigsegv_handler); +#ifdef LISP_FEATURE_SB_THREAD undoably_install_low_level_interrupt_handler(SIG_INTERRUPT_THREAD, handle_rt_signal); + undoably_install_low_level_interrupt_handler(SIG_STOP_FOR_GC, + sig_stop_for_gc_handler); +#endif undoably_install_low_level_interrupt_handler(SIGCONT, sigcont_handler); } Index: linux-os.h =================================================================== RCS file: /cvsroot/sbcl/sbcl/src/runtime/linux-os.h,v retrieving revision 1.8 retrieving revision 1.8.2.1 diff -u -d -r1.8 -r1.8.2.1 --- linux-os.h 16 Aug 2003 20:38:40 -0000 1.8 +++ linux-os.h 18 Aug 2003 11:06:19 -0000 1.8.2.1 @@ -38,4 +38,6 @@ #define SIG_MEMORY_FAULT SIGSEGV #define SIG_INTERRUPT_THREAD SIGRTMIN +#define SIG_STOP_FOR_GC (SIGRTMIN+1) + Index: thread.c =================================================================== RCS file: /cvsroot/sbcl/sbcl/src/runtime/thread.c,v retrieving revision 1.11.2.1 retrieving revision 1.11.2.2 diff -u -d -r1.11.2.1 -r1.11.2.2 --- thread.c 17 Aug 2003 23:20:18 -0000 1.11.2.1 +++ thread.c 18 Aug 2003 11:06:19 -0000 1.11.2.2 @@ -24,6 +24,7 @@ int dynamic_values_bytes=4096*sizeof(lispobj); /* same for all threads */ struct thread *all_threads; lispobj all_threads_lock; +int countdown_to_gc; extern struct interrupt_data * global_interrupt_data; void get_spinlock(lispobj *word,int value); @@ -290,18 +291,18 @@ union sigval sigval; sigval.sival_int=function; - sigqueue(pid, SIG_INTERRUPT_THREAD, sigval); + return sigqueue(pid, SIG_INTERRUPT_THREAD, sigval); } -int gc_stop_the_world() +void gc_stop_the_world() { /* stop all other threads by sending them SIG_STOP_FOR_GC */ - struct thread *th=arch_os_get_current_thread(); + struct thread *p,*th=arch_os_get_current_thread(); int countdown_to_gc=0; struct thread *tail=0; int finished=0; do { - get_spinlock(&all_threads_lock,th); + get_spinlock(&all_threads_lock,th->pid); if(tail!=all_threads) { /* new threads always get consed onto the front of all_threads, * and may be created by any thread that we haven't signalled @@ -319,13 +320,12 @@ release_spinlock(&all_threads_lock); sched_yield(); } while(!finished); - return 0; } -int gc_start_the_world() +void gc_start_the_world() { struct thread *p,*th=arch_os_get_current_thread(); - get_spinlock(&all_threads_lock); + get_spinlock(&all_threads_lock,th->pid); for(p=all_threads;p;p=p->next) { if(p==th) continue; kill(p->pid,SIGCONT); Index: x86-arch.h =================================================================== RCS file: /cvsroot/sbcl/sbcl/src/runtime/x86-arch.h,v retrieving revision 1.1.28.1 retrieving revision 1.1.28.2 diff -u -d -r1.1.28.1 -r1.1.28.2 --- x86-arch.h 17 Aug 2003 23:20:18 -0000 1.1.28.1 +++ x86-arch.h 18 Aug 2003 11:06:20 -0000 1.1.28.2 @@ -12,7 +12,7 @@ * here? (The answer wasn't obvious to me when merging the * architecture-abstracting patches for CSR's SPARC port. -- WHN 2002-02-15) */ -inline void +static inline void get_spinlock(lispobj *word,int value) { u32 eax=0; @@ -25,7 +25,7 @@ } while(eax!=0); } -inline void +static inline void release_spinlock(lispobj *word) { *word=0; Index: x86-linux-os.c =================================================================== RCS file: /cvsroot/sbcl/sbcl/src/runtime/x86-linux-os.c,v retrieving revision 1.15 retrieving revision 1.15.2.1 diff -u -d -r1.15 -r1.15.2.1 --- x86-linux-os.c 16 Aug 2003 20:38:40 -0000 1.15 +++ x86-linux-os.c 18 Aug 2003 11:06:20 -0000 1.15.2.1 @@ -51,9 +51,8 @@ u32 local_ldt_copy[LDT_ENTRIES*LDT_ENTRY_SIZE/sizeof(u32)]; -/* XXX this could be conditionally compiled based on some - * "debug-friendly" flag. But it doesn't really make stuff slower, - * just the runtime gets fractionally larger */ +/* This is never actually called, but it's great for calling from gdb when + * users have thread-related problems that maintainers can't duplicate */ void debug_get_ldt() { |