semaphores in the runtime
authorNikodemus Siivola <nikodemus@random-state.net>
Fri, 18 Nov 2011 20:37:22 +0000 (22:37 +0200)
committerNikodemus Siivola <nikodemus@random-state.net>
Mon, 5 Dec 2011 16:38:38 +0000 (18:38 +0200)
  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
src/runtime/bsd-os.c
src/runtime/darwin-os.c
src/runtime/darwin-os.h
src/runtime/dynbind.c
src/runtime/interrupt.c
src/runtime/ppc-assem.S
src/runtime/print.c
src/runtime/runtime.h
src/runtime/thread.c
src/runtime/thread.h

index 243ecc3..83fa84f 100644 (file)
   #!+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)
index 3f54aef..aa3ae3d 100644 (file)
@@ -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
 }
 
index 5e6642f..affbc72 100644 (file)
@@ -164,5 +164,10 @@ mach_fork() {
     }
 }
 
+void darwin_init(void)
+{
+    setup_mach_exception_handling_thread();
+}
+
 #endif
 
index be8640d..a308fb1 100644 (file)
@@ -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 */
index 9acc23b..32f79f0 100644 (file)
@@ -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)
index ea0880f..2e31562 100644 (file)
@@ -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) {
index 3f0dd99..128b3e0 100644 (file)
@@ -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
index 61d5a73..b4152fe 100644 (file)
@@ -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"
index b143eda..7ee4248 100644 (file)
@@ -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
index ac1ed79..1e8402a 100644 (file)
@@ -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));
                 }
index e62cb2b..71d3342 100644 (file)
@@ -4,6 +4,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <stddef.h>
+#include <errno.h>
+#include <stdio.h>
 #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 <semaphore.h>
+    typedef sem_t os_sem_t;
+# else
+#   include <mach/semaphore.h>
+    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; i<waitcount; i++)
+                os_sem_post(thread->state_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; i<waitcount; i++)
+                os_sem_post(thread->state_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;