pthread_cond_broadcast is not asynch signal safe
authorNikodemus Siivola <nikodemus@random-state.net>
Mon, 7 Nov 2011 12:49:57 +0000 (14:49 +0200)
committerNikodemus Siivola <nikodemus@random-state.net>
Thu, 17 Nov 2011 16:30:45 +0000 (18:30 +0200)
  AKA /less/ GC deadlocks on Darwin.

  To be specific, it can cause our GC to deadlock on Darwin, with all lisp
  threads spinning on the same global spinlock in the bowels of the
  pthread_cond_wait implementation. That was fun to figure out.

  The test (:interrupt-thread :interrupt-consing-child) is a good one
  for catching this: try to run it repeatedly under an earlier SBCL
  under Darwin, and sooner or later it will hang.

  ...with this commit, we're still using pthread_cond_broadcast, but
  blocking signals around the relevant bits, which --experimentally--
  makes the aforementioned test pass "somewhat more consistently".

  It can still hang, but those hangs seem to be related to deferrable
  signals being indefinitely blocked in one of the threads -- no idea
  as of yet why.

  Summa Summarum: this is a bit of a sorry bandaid, waiting for
  a better solution. (Probably using realtime semaphores, which
  /should/ be signal-handler safe.)

src/runtime/thread.h

index 249d226..e62cb2b 100644 (file)
@@ -30,28 +30,37 @@ static inline lispobj
 thread_state(struct thread *thread)
 {
     lispobj state;
+    sigset_t old;
+    block_blockable_signals(0, &old);
     pthread_mutex_lock(thread->state_lock);
     state = thread->state;
     pthread_mutex_unlock(thread->state_lock);
+    thread_sigmask(SIG_SETMASK,&old,0);
     return state;
 }
 
 static inline void
 set_thread_state(struct thread *thread, lispobj state)
 {
+    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);
 }
 
 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);
 }
 
 extern pthread_key_t lisp_thread;