1.0.1.1:
[sbcl.git] / src / runtime / thread.c
index f908428..2c3367c 100644 (file)
 #include <sys/wait.h>
 #endif
 
+#ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+#include <mach/mach_types.h>
+#endif
+
 #include "runtime.h"
 #include "validate.h"           /* for CONTROL_STACK_SIZE etc */
 #include "alloc.h"
 #define QUEUE_FREEABLE_THREAD_STACKS
 #endif
 
+#ifdef LISP_FEATURE_FREEBSD
+#define CREATE_CLEANUP_THREAD
+#define LOCK_CREATE_THREAD
+#endif
+
 #define ALIEN_STACK_SIZE (1*1024*1024) /* 1Mb size chosen at random */
 
 struct freeable_stack {
@@ -76,6 +87,9 @@ extern struct interrupt_data * global_interrupt_data;
 
 #ifdef LISP_FEATURE_SB_THREAD
 pthread_mutex_t all_threads_lock = PTHREAD_MUTEX_INITIALIZER;
+#ifdef LOCK_CREATE_THREAD
+static pthread_mutex_t create_thread_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
 #endif
 
 #if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64)
@@ -135,6 +149,7 @@ initial_thread_trampoline(struct thread *th)
 
 #ifdef QUEUE_FREEABLE_THREAD_STACKS
 
+static void
 queue_freeable_thread_stack(struct thread *thread_to_be_cleaned_up)
 {
      if (thread_to_be_cleaned_up) {
@@ -186,6 +201,45 @@ free_freeable_stacks() {
     }
 }
 
+#elif defined(CREATE_CLEANUP_THREAD)
+static void *
+cleanup_thread(void *arg)
+{
+    struct freeable_stack *freeable = arg;
+    pthread_t self = pthread_self();
+
+    FSHOW((stderr, "/cleaner thread(%p): joining %p\n",
+           self, freeable->os_thread));
+    gc_assert(pthread_join(freeable->os_thread, NULL) == 0);
+    FSHOW((stderr, "/cleaner thread(%p): free stack %p\n",
+           self, freeable->stack));
+    os_invalidate(freeable->stack, THREAD_STRUCT_SIZE);
+    free(freeable);
+
+    pthread_detach(self);
+
+    return NULL;
+}
+
+static void
+create_cleanup_thread(struct thread *thread_to_be_cleaned_up)
+{
+    pthread_t thread;
+    int result;
+
+    if (thread_to_be_cleaned_up) {
+        struct freeable_stack *freeable =
+            malloc(sizeof(struct freeable_stack));
+        gc_assert(freeable != NULL);
+        freeable->os_thread = thread_to_be_cleaned_up->os_thread;
+        freeable->stack =
+            (os_vm_address_t) thread_to_be_cleaned_up->control_stack_start;
+        result = pthread_create(&thread, NULL, cleanup_thread, freeable);
+        gc_assert(result == 0);
+        sched_yield();
+    }
+}
+
 #else
 static void
 free_thread_stack_later(struct thread *thread_to_be_cleaned_up)
@@ -265,8 +319,21 @@ new_thread_trampoline(struct thread *th)
     os_invalidate((os_vm_address_t)th->interrupt_data,
                   (sizeof (struct interrupt_data)));
 
+#ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
+    FSHOW((stderr, "Deallocating mach port %x\n", THREAD_STRUCT_TO_EXCEPTION_PORT(th)));
+    mach_port_move_member(mach_task_self(),
+                          THREAD_STRUCT_TO_EXCEPTION_PORT(th),
+                          MACH_PORT_NULL);
+    mach_port_deallocate(mach_task_self(),
+                         THREAD_STRUCT_TO_EXCEPTION_PORT(th));
+    mach_port_destroy(mach_task_self(),
+                      THREAD_STRUCT_TO_EXCEPTION_PORT(th));
+#endif
+
 #ifdef QUEUE_FREEABLE_THREAD_STACKS
     queue_freeable_thread_stack(th);
+#elif defined(CREATE_CLEANUP_THREAD)
+    create_cleanup_thread(th);
 #else
     free_thread_stack_later(th);
 #endif
@@ -301,6 +368,10 @@ create_thread_struct(lispobj initial_function) {
     int i;
 #endif
 
+#ifdef CREATE_CLEANUP_THREAD
+    /* Give a chance for cleanup threads to run. */
+    sched_yield();
+#endif
     /* may as well allocate all the spaces at once: it saves us from
      * having to decide what to do if only some of the allocations
      * succeed */
@@ -408,9 +479,20 @@ create_thread_struct(lispobj initial_function) {
     return th;
 }
 
+#ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
+mach_port_t setup_mach_exception_handling_thread();
+kern_return_t mach_thread_init(mach_port_t thread_exception_port);
+
+#endif
+
 void create_initial_thread(lispobj initial_function) {
     struct thread *th=create_thread_struct(initial_function);
     if(th) {
+#ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
+        kern_return_t ret;
+
+        setup_mach_exception_handling_thread();
+#endif
         initial_thread_trampoline(th); /* no return */
     } else lose("can't create initial thread\n");
 }
@@ -433,6 +515,11 @@ boolean create_os_thread(struct thread *th,os_thread_t *kid_tid)
 
     FSHOW_SIGNAL((stderr,"/create_os_thread: creating new thread\n"));
 
+#ifdef LOCK_CREATE_THREAD
+    retcode = pthread_mutex_lock(&create_thread_lock);
+    gc_assert(retcode == 0);
+    FSHOW_SIGNAL((stderr,"/create_os_thread: got lock\n"));
+#endif
     sigemptyset(&newset);
     /* Blocking deferrable signals is enough, no need to block
      * SIG_STOP_FOR_GC because the child process is not linked onto
@@ -462,10 +549,16 @@ boolean create_os_thread(struct thread *th,os_thread_t *kid_tid)
         }
         r=0;
     }
+
 #ifdef QUEUE_FREEABLE_THREAD_STACKS
     free_freeable_stacks();
 #endif
     thread_sigmask(SIG_SETMASK,&oldset,0);
+#ifdef LOCK_CREATE_THREAD
+    retcode = pthread_mutex_unlock(&create_thread_lock);
+    gc_assert(retcode == 0);
+    FSHOW_SIGNAL((stderr,"/create_os_thread: released lock\n"));
+#endif
     return r;
 }
 
@@ -536,6 +629,16 @@ void gc_stop_the_world()
 {
     struct thread *p,*th=arch_os_get_current_thread();
     int status, lock_ret;
+#ifdef LOCK_CREATE_THREAD
+    /* KLUDGE: Stopping the thread during pthread_create() causes deadlock
+     * on FreeBSD. */
+    FSHOW_SIGNAL((stderr,"/gc_stop_the_world:waiting on create_thread_lock, thread=%lu\n",
+                  th->os_thread));
+    lock_ret = pthread_mutex_lock(&create_thread_lock);
+    gc_assert(lock_ret == 0);
+    FSHOW_SIGNAL((stderr,"/gc_stop_the_world:got create_thread_lock, thread=%lu\n",
+                  th->os_thread));
+#endif
     FSHOW_SIGNAL((stderr,"/gc_stop_the_world:waiting on lock, thread=%lu\n",
                   th->os_thread));
     /* keep threads from starting while the world is stopped. */
@@ -612,6 +715,10 @@ void gc_start_the_world()
 
     lock_ret = pthread_mutex_unlock(&all_threads_lock);
     gc_assert(lock_ret == 0);
+#ifdef LOCK_CREATE_THREAD
+    lock_ret = pthread_mutex_unlock(&create_thread_lock);
+    gc_assert(lock_ret == 0);
+#endif
 
     FSHOW_SIGNAL((stderr,"/gc_start_the_world:end\n"));
 }