Fix threads on Darwin 10.8
authorPaul Khuong <pvk@pvk.ca>
Wed, 1 Aug 2012 22:00:34 +0000 (18:00 -0400)
committerPaul Khuong <pvk@pvk.ca>
Wed, 1 Aug 2012 22:10:09 +0000 (18:10 -0400)
 * We used to pun (64-bit) addresses into 32-bit mach_port_name.

 * We used to assume arbitrary 32-bit addresses were always
   acceptable mach port names.

 * Stop doing that. Instead, allocate small descriptors until one's
   address is an acceptable port name (inspired by CCL).

 * Also, keep a lock-free free-list of descriptors to skip the previous
   loop in common cases.

 * There are still some strange issues, but I can't tell if they're new,
   and they seem preferable to consistently lose-ing when spawning threads.

src/runtime/darwin-os.c
src/runtime/x86-64-darwin-os.c
src/runtime/x86-darwin-os.c

index 9cbf128..1103d8f 100644 (file)
@@ -27,6 +27,8 @@
 
 #ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
 #include <mach/mach.h>
+#include <libkern/OSAtomic.h>
+#include <stdlib.h>
 #endif
 
 char *
@@ -96,23 +98,80 @@ setup_mach_exception_handling_thread()
     return mach_exception_handling_thread;
 }
 
+struct exception_port_record
+{
+    struct thread * thread;
+    struct exception_port_record * next;
+};
+
+static OSQueueHead free_records = OS_ATOMIC_QUEUE_INIT;
+
+/* We can't depend on arbitrary addresses to be accepted as mach port
+ * names, particularly not on 64-bit platforms.  Instead, we allocate
+ * records that point to the thread struct, and loop until one is accepted
+ * as a port name.
+ *
+ * Threads are mapped to exception ports with a slot in the thread struct,
+ * and exception ports are casted to records that point to the corresponding
+ * thread.
+ *
+ * The lock-free free-list above is used as a cheap fast path.
+ */
+static mach_port_t
+find_receive_port(struct thread * thread)
+{
+    mach_port_t ret;
+    struct exception_port_record * curr, * to_free = NULL;
+    unsigned long i;
+    for (i = 1;; i++) {
+        curr = OSAtomicDequeue(&free_records, offsetof(struct exception_port_record, next));
+        if (curr == NULL) {
+            curr = calloc(1, sizeof(struct exception_port_record));
+            if (curr == NULL)
+                lose("unable to allocate exception_port_record\n");
+        }
+#ifdef LISP_FEATURE_X86_64
+        if ((mach_port_t)curr != (unsigned long)curr)
+            goto skip;
+#endif
+
+        if (mach_port_allocate_name(current_mach_task,
+                                    MACH_PORT_RIGHT_RECEIVE,
+                                    (mach_port_t)curr))
+            goto skip;
+        curr->thread = thread;
+        ret = (mach_port_t)curr;
+        break;
+        skip:
+        curr->next = to_free;
+        to_free = curr;
+        if ((i % 1024) == 0)
+            FSHOW((stderr, "Looped %lu times trying to allocate an exception port\n"));
+    }
+    while (to_free != NULL) {
+        struct exception_port_record * current = to_free;
+        to_free = to_free->next;
+        free(current);
+    }
+
+    FSHOW((stderr, "Allocated exception port %x for thread %p\n", ret, thread));
+
+    return ret;
+}
+
 /* tell the kernel that we want EXC_BAD_ACCESS exceptions sent to the
    exception port (which is being listened to do by the mach
    exception handling thread). */
 kern_return_t
-mach_thread_init(mach_port_t thread_exception_port)
+mach_lisp_thread_init(struct thread * thread)
 {
     kern_return_t ret;
-    mach_port_t current_mach_thread;
+    mach_port_t current_mach_thread, thread_exception_port;
 
     /* allocate a named port for the thread */
-    FSHOW((stderr, "Allocating mach port %x\n", thread_exception_port));
-    ret = mach_port_allocate_name(current_mach_task,
-                                  MACH_PORT_RIGHT_RECEIVE,
-                                  thread_exception_port);
-    if (ret) {
-        lose("mach_port_allocate_name failed with return_code %d\n", ret);
-    }
+    thread_exception_port
+        = thread->mach_port_name
+        = find_receive_port(thread);
 
     /* establish the right for the thread_exception_port to send messages */
     ret = mach_port_insert_right(current_mach_task,
@@ -149,31 +208,24 @@ mach_thread_init(mach_port_t thread_exception_port)
 }
 
 kern_return_t
-mach_lisp_thread_init(struct thread *thread) {
-    mach_port_t port = (mach_port_t) thread;
-    kern_return_t ret;
-    ret = mach_thread_init(port);
-    thread->mach_port_name = port;
-
-    return ret;
-}
-
-kern_return_t
 mach_lisp_thread_destroy(struct thread *thread) {
-    mach_port_t port = (mach_port_t) thread;
-
+    kern_return_t ret;
+    mach_port_t port = thread->mach_port_name;
     FSHOW((stderr, "Deallocating mach port %x\n", port));
     mach_port_move_member(current_mach_task, port, MACH_PORT_NULL);
     mach_port_deallocate(current_mach_task, port);
 
-    return mach_port_destroy(current_mach_task, port);
+    ret = mach_port_destroy(current_mach_task, port);
+    ((struct exception_port_record*)port)->thread = NULL;
+    OSAtomicEnqueue(&free_records, (void*)port, offsetof(struct exception_port_record, next));
+
+    return ret;
 }
 
 void
 setup_mach_exceptions() {
-    mach_port_t port = (mach_port_t) all_threads;
     setup_mach_exception_handling_thread();
-    mach_thread_init(port);
+    mach_lisp_thread_init(all_threads);
 }
 
 pid_t
index 8a25e63..6378d7b 100644 (file)
@@ -325,9 +325,10 @@ catch_exception_raise(mach_port_t exception_port,
 
     os_vm_address_t addr;
 
-    struct thread *th = (struct thread*) exception_port;
+    struct thread *th;
 
     FSHOW((stderr,"/entering catch_exception_raise with exception: %d\n", exception));
+    th = *(struct thread**)exception_port;
 
     switch (exception) {
 
@@ -346,8 +347,6 @@ catch_exception_raise(mach_port_t exception_port,
                                (thread_state_t)&exception_state,
                                &exception_state_count);
         addr = (void*)exception_state.faultvaddr;
-
-
         /* note the os_context hackery here.  When the signal handler returns,
          * it won't go back to what it was doing ... */
         if(addr >= CONTROL_STACK_GUARD_PAGE(th) &&
index edcd85c..323e549 100644 (file)
@@ -395,10 +395,10 @@ catch_exception_raise(mach_port_t exception_port,
     siginfo_t siginfo;
     kern_return_t ret, dealloc_ret;
 
-    struct thread *th = (struct thread*) exception_port;
+    struct thread *th;
 
     FSHOW((stderr,"/entering catch_exception_raise with exception: %d\n", exception));
-
+    th = *(struct thread**)exception_port;
     /* Get state and info */
     state_count = x86_THREAD_STATE32_COUNT;
     if ((ret = thread_get_state(thread,