+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;
+}
+