2 * This is the Darwin incarnation of OS-dependent routines. See also
7 * This software is part of the SBCL system. See the README file for
10 * This software is derived from the CMU CL system, which was
11 * written at Carnegie Mellon University and released into the
12 * public domain. The software is in the public domain and is
13 * provided with absolutely no warranty. See the COPYING and CREDITS
14 * files for more information.
23 #include <mach-o/dyld.h>
28 #ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
29 #include <mach/mach.h>
30 #include <libkern/OSAtomic.h>
35 os_get_runtime_executable_path(int external)
37 char path[PATH_MAX + 1];
38 uint32_t size = sizeof(path);
40 if (_NSGetExecutablePath(path, &size) == -1)
43 return copied_string(path);
46 #ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
48 /* exc_server handles mach exception messages from the kernel and
49 * calls catch exception raise. We use the system-provided
50 * mach_msg_server, which, I assume, calls exc_server in a loop.
53 extern boolean_t exc_server();
56 mach_exception_handler(void *port)
58 mach_msg_server(exc_server, 2048, (mach_port_t) port, 0);
59 /* mach_msg_server should never return, but it should dispatch mach
60 * exceptions to our catch_exception_raise function
62 lose("mach_msg_server returned");
65 /* Sets up the thread that will listen for mach exceptions. note that
66 the exception handlers will be run on this thread. This is
67 different from the BSD-style signal handling situation in which the
68 signal handlers run in the relevant thread directly. */
70 mach_port_t mach_exception_handler_port_set = MACH_PORT_NULL;
71 mach_port_t current_mach_task = MACH_PORT_NULL;
74 setup_mach_exception_handling_thread()
77 pthread_t mach_exception_handling_thread = NULL;
80 current_mach_task = mach_task_self();
82 /* allocate a mach_port for this process */
83 ret = mach_port_allocate(current_mach_task,
84 MACH_PORT_RIGHT_PORT_SET,
85 &mach_exception_handler_port_set);
87 /* create the thread that will receive the mach exceptions */
89 FSHOW((stderr, "Creating mach_exception_handler thread!\n"));
91 pthread_attr_init(&attr);
92 pthread_create(&mach_exception_handling_thread,
94 mach_exception_handler,
95 (void*) mach_exception_handler_port_set);
96 pthread_attr_destroy(&attr);
98 return mach_exception_handling_thread;
101 struct exception_port_record
103 struct thread * thread;
104 struct exception_port_record * next;
107 static OSQueueHead free_records = OS_ATOMIC_QUEUE_INIT;
109 /* We can't depend on arbitrary addresses to be accepted as mach port
110 * names, particularly not on 64-bit platforms. Instead, we allocate
111 * records that point to the thread struct, and loop until one is accepted
114 * Threads are mapped to exception ports with a slot in the thread struct,
115 * and exception ports are casted to records that point to the corresponding
118 * The lock-free free-list above is used as a cheap fast path.
121 find_receive_port(struct thread * thread)
124 struct exception_port_record * curr, * to_free = NULL;
127 curr = OSAtomicDequeue(&free_records, offsetof(struct exception_port_record, next));
129 curr = calloc(1, sizeof(struct exception_port_record));
131 lose("unable to allocate exception_port_record\n");
133 #ifdef LISP_FEATURE_X86_64
134 if ((mach_port_t)curr != (unsigned long)curr)
138 if (mach_port_allocate_name(current_mach_task,
139 MACH_PORT_RIGHT_RECEIVE,
142 curr->thread = thread;
143 ret = (mach_port_t)curr;
146 curr->next = to_free;
149 FSHOW((stderr, "Looped %lu times trying to allocate an exception port\n"));
151 while (to_free != NULL) {
152 struct exception_port_record * current = to_free;
153 to_free = to_free->next;
157 FSHOW((stderr, "Allocated exception port %x for thread %p\n", ret, thread));
162 /* tell the kernel that we want EXC_BAD_ACCESS exceptions sent to the
163 exception port (which is being listened to do by the mach
164 exception handling thread). */
166 mach_lisp_thread_init(struct thread * thread)
169 mach_port_t current_mach_thread, thread_exception_port;
171 /* allocate a named port for the thread */
172 thread_exception_port
173 = thread->mach_port_name
174 = find_receive_port(thread);
176 /* establish the right for the thread_exception_port to send messages */
177 ret = mach_port_insert_right(current_mach_task,
178 thread_exception_port,
179 thread_exception_port,
180 MACH_MSG_TYPE_MAKE_SEND);
182 lose("mach_port_insert_right failed with return_code %d\n", ret);
185 current_mach_thread = mach_thread_self();
186 ret = thread_set_exception_ports(current_mach_thread,
187 EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION,
188 thread_exception_port,
192 lose("thread_set_exception_ports failed with return_code %d\n", ret);
195 ret = mach_port_deallocate (current_mach_task, current_mach_thread);
197 lose("mach_port_deallocate failed with return_code %d\n", ret);
200 ret = mach_port_move_member(current_mach_task,
201 thread_exception_port,
202 mach_exception_handler_port_set);
204 lose("mach_port_move_member failed with return_code %d\n", ret);
211 mach_lisp_thread_destroy(struct thread *thread) {
213 mach_port_t port = thread->mach_port_name;
214 FSHOW((stderr, "Deallocating mach port %x\n", port));
215 mach_port_move_member(current_mach_task, port, MACH_PORT_NULL);
216 mach_port_deallocate(current_mach_task, port);
218 ret = mach_port_destroy(current_mach_task, port);
219 ((struct exception_port_record*)port)->thread = NULL;
220 OSAtomicEnqueue(&free_records, (void*)port, offsetof(struct exception_port_record, next));
226 setup_mach_exceptions() {
227 setup_mach_exception_handling_thread();
228 mach_lisp_thread_init(all_threads);
235 setup_mach_exceptions();
243 void darwin_init(void)
245 #ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
246 setup_mach_exception_handling_thread();
251 #ifdef LISP_FEATURE_SB_THREAD
254 os_sem_init(os_sem_t *sem, unsigned int value)
256 if (KERN_SUCCESS!=semaphore_create(current_mach_task, sem, SYNC_POLICY_FIFO, (int)value))
257 lose("os_sem_init(%p): %s", sem, strerror(errno));
261 os_sem_wait(os_sem_t *sem, char *what)
265 FSHOW((stderr, "%s: os_sem_wait(%p)\n", what, sem));
266 ret = semaphore_wait(*sem);
267 FSHOW((stderr, "%s: os_sem_wait(%p) => %s\n", what, sem,
268 KERN_SUCCESS==ret ? "ok" : strerror(errno)));
272 /* It is unclear just when we can get this, but a sufficiently
273 * long wait seems to do that, at least sometimes.
275 * However, a wait that long is definitely abnormal for the
276 * GC, so we complain before retrying.
278 case KERN_OPERATION_TIMED_OUT:
279 fprintf(stderr, "%s: os_sem_wait(%p): %s", what, sem, strerror(errno));
280 /* This is analogous to POSIX EINTR. */
284 lose("%s: os_sem_wait(%p): %lu, %s", what, sem, ret, strerror(errno));
289 os_sem_post(os_sem_t *sem, char *what)
291 if (KERN_SUCCESS!=semaphore_signal(*sem))
292 lose("%s: os_sem_post(%p): %s", what, sem, strerror(errno));
293 FSHOW((stderr, "%s: os_sem_post(%p) ok\n", what, sem));
297 os_sem_destroy(os_sem_t *sem)
299 if (-1==semaphore_destroy(current_mach_task, *sem))
300 lose("os_sem_destroy(%p): %s", sem, strerror(errno));