From 8340bf74c31b29e9552ef8f705b6e1298547c6ab Mon Sep 17 00:00:00 2001 From: Nikodemus Siivola Date: Fri, 18 Nov 2011 22:37:22 +0200 Subject: [PATCH] semaphores in the runtime Trivial refactorings: * Rename STATE_SUSPENDED STATE_STOPPED for elegance. (Spells with the same number of letters as STATE_RUNNING, things line up nicer.) * Re-express make_fixnum in terms of MAKE_FIXNUM so that we can use the latter to define STATE_* names in a manner acceptable to use in switch-statements. * Move Mach exception handling initialization to darwin_init from create_initial_thread so that current_mach_task gets initialized before the first thread struct is initialized. The Beef: Replace condition variables in the runtime with semaphores. On most platforms use sem_t, but on Darwin use semaphore_t. Hide the difference behind, os_sem_t, os_sem_init, os_sem_destroy, os_sem_post, and os_sem_wait. POSIX realtime semaphores are supposedly safe to use in signal handlers, unlike condition variables -- and experimentally at least Mach semaphores on Darwin are a lot less prone to problems. (Our pthread mutex usage isn't quite kosher either, but it's the pthread_cond_wait and pthread_cond_broadcast pair that seemed to be causing most of the trouble.) --- src/compiler/generic/objdef.lisp | 10 ++- src/runtime/bsd-os.c | 2 + src/runtime/darwin-os.c | 5 ++ src/runtime/darwin-os.h | 2 + src/runtime/dynbind.c | 1 - src/runtime/interrupt.c | 4 +- src/runtime/ppc-assem.S | 2 +- src/runtime/print.c | 1 + src/runtime/runtime.h | 5 +- src/runtime/thread.c | 28 ++++--- src/runtime/thread.h | 168 +++++++++++++++++++++++++++++++++----- 11 files changed, 189 insertions(+), 39 deletions(-) diff --git a/src/compiler/generic/objdef.lisp b/src/compiler/generic/objdef.lisp index 243ecc3..83fa84f 100644 --- a/src/compiler/generic/objdef.lisp +++ b/src/compiler/generic/objdef.lisp @@ -377,9 +377,15 @@ #!+sb-thread (os-attr :c-type "pthread_attr_t *" :length #!+alpha 2 #!-alpha 1) #!+sb-thread - (state-lock :c-type "pthread_mutex_t *" :length #!+alpha 2 #!-alpha 1) + (state-sem :c-type "os_sem_t *" :length #!+alpha 2 #!-alpha 1) #!+sb-thread - (state-cond :c-type "pthread_cond_t *" :length #!+alpha 2 #!-alpha 1) + (state-not-running-sem :c-type "os_sem_t *" :length #!+alpha 2 #!-alpha 1) + #!+sb-thread + (state-not-running-waitcount :c-type "int" :length 1) + #!+sb-thread + (state-not-stopped-sem :c-type "os_sem_t *" :length #!+alpha 2 #!-alpha 1) + #!+sb-thread + (state-not-stopped-waitcount :c-type "int" :length 1) (binding-stack-start :c-type "lispobj *" :length #!+alpha 2 #!-alpha 1) (binding-stack-pointer :c-type "lispobj *" :length #!+alpha 2 #!-alpha 1) (control-stack-start :c-type "lispobj *" :length #!+alpha 2 #!-alpha 1) diff --git a/src/runtime/bsd-os.c b/src/runtime/bsd-os.c index 3f54aef..aa3ae3d 100644 --- a/src/runtime/bsd-os.c +++ b/src/runtime/bsd-os.c @@ -90,6 +90,8 @@ os_init(char *argv[], char *envp[]) freebsd_init(); #elif defined(__OpenBSD__) openbsd_init(); +#elif defined(LISP_FEATURE_DARWIN) + darwin_init(); #endif } diff --git a/src/runtime/darwin-os.c b/src/runtime/darwin-os.c index 5e6642f..affbc72 100644 --- a/src/runtime/darwin-os.c +++ b/src/runtime/darwin-os.c @@ -164,5 +164,10 @@ mach_fork() { } } +void darwin_init(void) +{ + setup_mach_exception_handling_thread(); +} + #endif diff --git a/src/runtime/darwin-os.h b/src/runtime/darwin-os.h index be8640d..a308fb1 100644 --- a/src/runtime/darwin-os.h +++ b/src/runtime/darwin-os.h @@ -38,4 +38,6 @@ typedef ucontext_t os_context_t; extern mach_port_t current_mach_task; #endif +void darwin_init(void); + #endif /* _DARWIN_OS_H */ diff --git a/src/runtime/dynbind.c b/src/runtime/dynbind.c index 9acc23b..32f79f0 100644 --- a/src/runtime/dynbind.c +++ b/src/runtime/dynbind.c @@ -24,7 +24,6 @@ #include "pseudo-atomic.h" #include "genesis/symbol.h" #include "genesis/binding.h" -#include "genesis/thread.h" #include "genesis/static-symbols.h" void bind_variable(lispobj symbol, lispobj value, void *th) diff --git a/src/runtime/interrupt.c b/src/runtime/interrupt.c index ea0880f..2e31562 100644 --- a/src/runtime/interrupt.c +++ b/src/runtime/interrupt.c @@ -1253,7 +1253,7 @@ sig_stop_for_gc_handler(int signal, siginfo_t *info, os_context_t *context) fixnum_value(thread->state)); } - set_thread_state(thread,STATE_SUSPENDED); + set_thread_state(thread,STATE_STOPPED); FSHOW_SIGNAL((stderr,"suspended\n")); /* While waiting for gc to finish occupy ourselves with zeroing @@ -1262,7 +1262,7 @@ sig_stop_for_gc_handler(int signal, siginfo_t *info, os_context_t *context) * actually a must. */ scrub_control_stack(); - wait_for_thread_state_change(thread, STATE_SUSPENDED); + wait_for_thread_state_change(thread, STATE_STOPPED); FSHOW_SIGNAL((stderr,"resumed\n")); if(thread_state(thread)!=STATE_RUNNING) { diff --git a/src/runtime/ppc-assem.S b/src/runtime/ppc-assem.S index 3f0dd99..128b3e0 100644 --- a/src/runtime/ppc-assem.S +++ b/src/runtime/ppc-assem.S @@ -10,7 +10,7 @@ #include "genesis/funcallable-instance.h" #include "genesis/static-symbols.h" #ifdef LISP_FEATURE_SB_THREAD -#include "genesis/thread.h" +#include "thread.h" #endif #ifdef LISP_FEATURE_DARWIN diff --git a/src/runtime/print.c b/src/runtime/print.c index 61d5a73..b4152fe 100644 --- a/src/runtime/print.c +++ b/src/runtime/print.c @@ -33,6 +33,7 @@ #include "os.h" #include "gencgc-alloc-region.h" /* genesis/thread.h needs this */ #include "genesis/static-symbols.h" +#include "thread.h" /* genesis/primitive-objects.h needs this */ #include "genesis/primitive-objects.h" #include "genesis/static-symbols.h" #include "genesis/tagnames.h" diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index b143eda..7ee4248 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -120,8 +120,10 @@ typedef pid_t os_thread_t; alpha64 has arrived, all this nastiness can go away */ #if 64 == N_WORD_BITS #define LOW_WORD(c) ((pointer_sized_uint_t)c) +#define OBJ_FMTX "lx" typedef unsigned long lispobj; #else +#define OBJ_FMTX "x" #define LOW_WORD(c) ((long)(c) & 0xFFFFFFFFL) /* fake it on alpha32 */ typedef unsigned int lispobj; @@ -206,10 +208,11 @@ make_lispobj(void *o, int low_tag) return LOW_WORD(o) | low_tag; } +#define MAKE_FIXNUM(n) (n << N_FIXNUM_TAG_BITS) static inline lispobj make_fixnum(long n) { - return n << N_FIXNUM_TAG_BITS; + return MAKE_FIXNUM(n); } static inline long diff --git a/src/runtime/thread.c b/src/runtime/thread.c index ac1ed79..1e8402a 100644 --- a/src/runtime/thread.c +++ b/src/runtime/thread.c @@ -158,7 +158,7 @@ initial_thread_trampoline(struct thread *th) #ifdef LISP_FEATURE_SB_THREAD #define THREAD_STATE_LOCK_SIZE \ - (sizeof(pthread_mutex_t))+(sizeof(pthread_cond_t)) + ((sizeof(os_sem_t))+(sizeof(os_sem_t))+(sizeof(os_sem_t))) #else #define THREAD_STATE_LOCK_SIZE 0 #endif @@ -310,8 +310,9 @@ new_thread_trampoline(struct thread *th) gc_assert(lock_ret == 0); if(th->tls_cookie>=0) arch_os_thread_cleanup(th); - pthread_mutex_destroy(th->state_lock); - pthread_cond_destroy(th->state_cond); + os_sem_destroy(th->state_sem); + os_sem_destroy(th->state_not_running_sem); + os_sem_destroy(th->state_not_stopped_sem); os_invalidate((os_vm_address_t)th->interrupt_data, (sizeof (struct interrupt_data))); @@ -430,12 +431,16 @@ create_thread_struct(lispobj initial_function) { th->os_thread=0; #ifdef LISP_FEATURE_SB_THREAD th->os_attr=malloc(sizeof(pthread_attr_t)); - th->state_lock=(pthread_mutex_t *)((void *)th->alien_stack_start + - ALIEN_STACK_SIZE); - pthread_mutex_init(th->state_lock, NULL); - th->state_cond=(pthread_cond_t *)((void *)th->state_lock + - (sizeof(pthread_mutex_t))); - pthread_cond_init(th->state_cond, NULL); + th->state_sem=(os_sem_t *)((void *)th->alien_stack_start + ALIEN_STACK_SIZE); + th->state_not_running_sem=(os_sem_t *) + ((void *)th->state_sem + (sizeof(os_sem_t))); + th->state_not_stopped_sem=(os_sem_t *) + ((void *)th->state_not_running_sem + (sizeof(os_sem_t))); + th->state_not_running_waitcount = 0; + th->state_not_stopped_waitcount = 0; + os_sem_init(th->state_sem, 1); + os_sem_init(th->state_not_running_sem, 0); + os_sem_init(th->state_not_stopped_sem, 0); #endif th->state=STATE_RUNNING; #ifdef LISP_FEATURE_STACK_GROWS_DOWNWARD_NOT_UPWARD @@ -523,9 +528,6 @@ void create_initial_thread(lispobj initial_function) { pthread_key_create(&lisp_thread, 0); #endif if(th) { -#ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER - setup_mach_exception_handling_thread(); -#endif initial_thread_trampoline(th); /* no return */ } else lose("can't create initial thread\n"); } @@ -679,7 +681,7 @@ void gc_start_the_world() if (p!=th) { lispobj state = thread_state(p); if (state != STATE_DEAD) { - if(state != STATE_SUSPENDED) { + if(state != STATE_STOPPED) { lose("gc_start_the_world: wrong thread state is %d\n", fixnum_value(state)); } diff --git a/src/runtime/thread.h b/src/runtime/thread.h index e62cb2b..71d3342 100644 --- a/src/runtime/thread.h +++ b/src/runtime/thread.h @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include "sbcl.h" #include "globals.h" #include "runtime.h" @@ -15,52 +17,180 @@ struct alloc_region { }; #endif #include "genesis/symbol.h" #include "genesis/static-symbols.h" + +#ifdef LISP_FEATURE_SB_THREAD +# ifndef LISP_FEATURE_DARWIN +# include + typedef sem_t os_sem_t; +# else +# include + typedef semaphore_t os_sem_t; +# endif +#endif + #include "genesis/thread.h" #include "genesis/fdefn.h" #include "interrupt.h" -#define STATE_RUNNING (make_fixnum(1)) -#define STATE_SUSPENDED (make_fixnum(2)) -#define STATE_DEAD (make_fixnum(3)) +#define STATE_RUNNING MAKE_FIXNUM(1) +#define STATE_STOPPED MAKE_FIXNUM(2) +#define STATE_DEAD MAKE_FIXNUM(3) #ifdef LISP_FEATURE_SB_THREAD +# ifndef LISP_FEATURE_DARWIN + +static inline void +os_sem_init(os_sem_t *sem, unsigned int value) +{ + if (-1==sem_init(sem, 0, value)) + lose("os_sem_init(%p, %u): %s", sem, value, strerror(errno)); + FSHOW((stderr, "os_sem_init(%p, %u)\n", sem, value)); +} + +static inline void +os_sem_wait(os_sem_t *sem, char *what) +{ + FSHOW((stderr, "%s: os_sem_wait(%p) ...\n", what, sem)); + while (-1 == sem_wait(sem)) + if (EINTR!=errno) + lose("%s: os_sem_wait(%p): %s", what, sem, strerror(errno)); + FSHOW((stderr, "%s: os_sem_wait(%p) => ok\n", what, sem)); +} + +static inline void +os_sem_post(sem_t *sem, char *what) +{ + if (-1 == sem_post(sem)) + lose("%s: os_sem_post(%p): %s", what, sem, strerror(errno)); + FSHOW((stderr, "%s: os_sem_post(%p)\n", what, sem)); +} + +static inline void +os_sem_destroy(os_sem_t *sem) +{ + if (-1==sem_destroy(sem)) + lose("os_sem_destroy(%p): %s", sem, strerror(errno)); +} + +# else + +static inline void +os_sem_init(os_sem_t *sem, unsigned int value) +{ + if (KERN_SUCCESS!=semaphore_create(current_mach_task, sem, SYNC_POLICY_FIFO, (int)value)) + lose("os_sem_init(%p): %s", sem, strerror(errno)); +} + +static inline void +os_sem_wait(os_sem_t *sem, char *what) +{ + kern_return_t ret; + restart: + FSHOW((stderr, "%s: os_sem_wait(%p)\n", what, sem)); + ret = semaphore_wait(*sem); + FSHOW((stderr, "%s: os_sem_wait(%p) => %s\n", what, sem, + KERN_SUCCESS==ret ? "ok" : strerror(errno))); + switch (ret) { + case KERN_SUCCESS: + return; + case KERN_OPERATION_TIMED_OUT: + fprintf(stderr, "%s: os_sem_wait(%p): %s", what, sem, strerror(errno)); + goto restart; + default: + lose("%s: os_sem_wait(%p): %s", what, sem, strerror(errno)); + } +} + +static inline void +os_sem_post(os_sem_t *sem, char *what) +{ + if (KERN_SUCCESS!=semaphore_signal(*sem)) + lose("%s: os_sem_post(%p): %s", what, sem, strerror(errno)); + FSHOW((stderr, "%s: os_sem_post(%p) ok\n", what, sem)); +} + +static inline void +os_sem_destroy(os_sem_t *sem) +{ + if (-1==semaphore_destroy(current_mach_task, *sem)) + lose("os_sem_destroy(%p): %s", sem, strerror(errno)); +} + +# endif + /* Only access thread state with blockables blocked. */ static inline lispobj thread_state(struct thread *thread) { lispobj state; sigset_t old; - block_blockable_signals(0, &old); - pthread_mutex_lock(thread->state_lock); + block_blockable_signals(NULL, &old); + os_sem_wait(thread->state_sem, "thread_state"); state = thread->state; - pthread_mutex_unlock(thread->state_lock); - thread_sigmask(SIG_SETMASK,&old,0); + os_sem_post(thread->state_sem, "thread_state"); + thread_sigmask(SIG_SETMASK, &old, NULL); return state; } static inline void set_thread_state(struct thread *thread, lispobj state) { + int i, waitcount = 0; sigset_t old; - block_blockable_signals(0, &old); - pthread_mutex_lock(thread->state_lock); - thread->state = state; - pthread_cond_broadcast(thread->state_cond); - pthread_mutex_unlock(thread->state_lock); - thread_sigmask(SIG_SETMASK,&old,0); + block_blockable_signals(NULL, &old); + os_sem_wait(thread->state_sem, "set_thread_state"); + if (thread->state != state) { + if ((STATE_STOPPED==state) || + (STATE_DEAD==state)) { + waitcount = thread->state_not_running_waitcount; + thread->state_not_running_waitcount = 0; + for (i=0; istate_not_running_sem, "set_thread_state (not running)"); + } + if ((STATE_RUNNING==state) || + (STATE_DEAD==state)) { + waitcount = thread->state_not_stopped_waitcount; + thread->state_not_stopped_waitcount = 0; + for (i=0; istate_not_stopped_sem, "set_thread_state (not stopped)"); + } + thread->state = state; + } + os_sem_post(thread->state_sem, "set_thread_state"); + thread_sigmask(SIG_SETMASK, &old, NULL); } static inline void wait_for_thread_state_change(struct thread *thread, lispobj state) { sigset_t old; - block_blockable_signals(0, &old); - pthread_mutex_lock(thread->state_lock); - while (thread->state == state) - pthread_cond_wait(thread->state_cond, thread->state_lock); - pthread_mutex_unlock(thread->state_lock); - thread_sigmask(SIG_SETMASK,&old,0); + os_sem_t *wait_sem; + block_blockable_signals(NULL, &old); + start: + os_sem_wait(thread->state_sem, "wait_for_thread_state_change"); + if (thread->state == state) { + switch (state) { + case STATE_RUNNING: + wait_sem = thread->state_not_running_sem; + thread->state_not_running_waitcount++; + break; + case STATE_STOPPED: + wait_sem = thread->state_not_stopped_sem; + thread->state_not_stopped_waitcount++; + break; + default: + lose("Invalid state in wait_for_thread_state_change: "OBJ_FMTX"\n", state); + } + } else { + wait_sem = NULL; + } + os_sem_post(thread->state_sem, "wait_for_thread_state_change"); + if (wait_sem) { + os_sem_wait(wait_sem, "wait_for_thread_state_change"); + goto start; + } + thread_sigmask(SIG_SETMASK, &old, NULL); } extern pthread_key_t lisp_thread; -- 1.7.10.4