From: Paul Khuong Date: Wed, 1 Aug 2012 22:00:34 +0000 (-0400) Subject: Fix threads on Darwin 10.8 X-Git-Url: http://repo.macrolet.net/gitweb/?a=commitdiff_plain;h=578362575fc2112b828597cc1025e3ead43d43ba;p=sbcl.git Fix threads on Darwin 10.8 * 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. --- diff --git a/src/runtime/darwin-os.c b/src/runtime/darwin-os.c index 9cbf128..1103d8f 100644 --- a/src/runtime/darwin-os.c +++ b/src/runtime/darwin-os.c @@ -27,6 +27,8 @@ #ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER #include +#include +#include #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 diff --git a/src/runtime/x86-64-darwin-os.c b/src/runtime/x86-64-darwin-os.c index 8a25e63..6378d7b 100644 --- a/src/runtime/x86-64-darwin-os.c +++ b/src/runtime/x86-64-darwin-os.c @@ -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) && diff --git a/src/runtime/x86-darwin-os.c b/src/runtime/x86-darwin-os.c index edcd85c..323e549 100644 --- a/src/runtime/x86-darwin-os.c +++ b/src/runtime/x86-darwin-os.c @@ -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,