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>
34 #if defined(LISP_FEATURE_SB_WTIMER)
35 # include <sys/types.h>
36 # include <sys/event.h>
37 # include <sys/time.h>
41 os_get_runtime_executable_path(int external
)
43 char path
[PATH_MAX
+ 1];
44 uint32_t size
= sizeof(path
);
46 if (_NSGetExecutablePath(path
, &size
) == -1)
49 return copied_string(path
);
52 #ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
54 /* exc_server handles mach exception messages from the kernel and
55 * calls catch exception raise. We use the system-provided
56 * mach_msg_server, which, I assume, calls exc_server in a loop.
59 extern boolean_t
exc_server();
62 mach_exception_handler(void *port
)
64 mach_msg_server(exc_server
, 2048, (mach_port_t
) port
, 0);
65 /* mach_msg_server should never return, but it should dispatch mach
66 * exceptions to our catch_exception_raise function
68 lose("mach_msg_server returned");
71 /* Sets up the thread that will listen for mach exceptions. note that
72 the exception handlers will be run on this thread. This is
73 different from the BSD-style signal handling situation in which the
74 signal handlers run in the relevant thread directly. */
76 mach_port_t mach_exception_handler_port_set
= MACH_PORT_NULL
;
77 mach_port_t current_mach_task
= MACH_PORT_NULL
;
80 setup_mach_exception_handling_thread()
83 pthread_t mach_exception_handling_thread
= NULL
;
86 current_mach_task
= mach_task_self();
88 /* allocate a mach_port for this process */
89 ret
= mach_port_allocate(current_mach_task
,
90 MACH_PORT_RIGHT_PORT_SET
,
91 &mach_exception_handler_port_set
);
93 /* create the thread that will receive the mach exceptions */
95 FSHOW((stderr
, "Creating mach_exception_handler thread!\n"));
97 pthread_attr_init(&attr
);
98 pthread_create(&mach_exception_handling_thread
,
100 mach_exception_handler
,
101 (void*) mach_exception_handler_port_set
);
102 pthread_attr_destroy(&attr
);
104 return mach_exception_handling_thread
;
107 struct exception_port_record
109 struct thread
* thread
;
110 struct exception_port_record
* next
;
113 static OSQueueHead free_records
= OS_ATOMIC_QUEUE_INIT
;
115 /* We can't depend on arbitrary addresses to be accepted as mach port
116 * names, particularly not on 64-bit platforms. Instead, we allocate
117 * records that point to the thread struct, and loop until one is accepted
120 * Threads are mapped to exception ports with a slot in the thread struct,
121 * and exception ports are casted to records that point to the corresponding
124 * The lock-free free-list above is used as a cheap fast path.
127 find_receive_port(struct thread
* thread
)
130 struct exception_port_record
* curr
, * to_free
= NULL
;
133 curr
= OSAtomicDequeue(&free_records
, offsetof(struct exception_port_record
, next
));
135 curr
= calloc(1, sizeof(struct exception_port_record
));
137 lose("unable to allocate exception_port_record\n");
139 #ifdef LISP_FEATURE_X86_64
140 if ((mach_port_t
)curr
!= (unsigned long)curr
)
144 if (mach_port_allocate_name(current_mach_task
,
145 MACH_PORT_RIGHT_RECEIVE
,
148 curr
->thread
= thread
;
149 ret
= (mach_port_t
)curr
;
152 curr
->next
= to_free
;
155 FSHOW((stderr
, "Looped %lu times trying to allocate an exception port\n"));
157 while (to_free
!= NULL
) {
158 struct exception_port_record
* current
= to_free
;
159 to_free
= to_free
->next
;
163 FSHOW((stderr
, "Allocated exception port %x for thread %p\n", ret
, thread
));
168 /* tell the kernel that we want EXC_BAD_ACCESS exceptions sent to the
169 exception port (which is being listened to do by the mach
170 exception handling thread). */
172 mach_lisp_thread_init(struct thread
* thread
)
175 mach_port_t current_mach_thread
, thread_exception_port
;
177 /* allocate a named port for the thread */
178 thread_exception_port
179 = thread
->mach_port_name
180 = find_receive_port(thread
);
182 /* establish the right for the thread_exception_port to send messages */
183 ret
= mach_port_insert_right(current_mach_task
,
184 thread_exception_port
,
185 thread_exception_port
,
186 MACH_MSG_TYPE_MAKE_SEND
);
188 lose("mach_port_insert_right failed with return_code %d\n", ret
);
191 current_mach_thread
= mach_thread_self();
192 ret
= thread_set_exception_ports(current_mach_thread
,
193 EXC_MASK_BAD_ACCESS
| EXC_MASK_BAD_INSTRUCTION
,
194 thread_exception_port
,
198 lose("thread_set_exception_ports failed with return_code %d\n", ret
);
201 ret
= mach_port_deallocate (current_mach_task
, current_mach_thread
);
203 lose("mach_port_deallocate failed with return_code %d\n", ret
);
206 ret
= mach_port_move_member(current_mach_task
,
207 thread_exception_port
,
208 mach_exception_handler_port_set
);
210 lose("mach_port_move_member failed with return_code %d\n", ret
);
217 mach_lisp_thread_destroy(struct thread
*thread
) {
219 mach_port_t port
= thread
->mach_port_name
;
220 FSHOW((stderr
, "Deallocating mach port %x\n", port
));
221 mach_port_move_member(current_mach_task
, port
, MACH_PORT_NULL
);
222 mach_port_deallocate(current_mach_task
, port
);
224 ret
= mach_port_destroy(current_mach_task
, port
);
225 ((struct exception_port_record
*)port
)->thread
= NULL
;
226 OSAtomicEnqueue(&free_records
, (void*)port
, offsetof(struct exception_port_record
, next
));
232 setup_mach_exceptions() {
233 setup_mach_exception_handling_thread();
234 mach_lisp_thread_init(all_threads
);
241 setup_mach_exceptions();
249 void darwin_init(void)
251 #ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
252 setup_mach_exception_handling_thread();
257 #ifdef LISP_FEATURE_SB_THREAD
260 os_sem_init(os_sem_t
*sem
, unsigned int value
)
262 if (KERN_SUCCESS
!=semaphore_create(current_mach_task
, sem
, SYNC_POLICY_FIFO
, (int)value
))
263 lose("os_sem_init(%p): %s", sem
, strerror(errno
));
267 os_sem_wait(os_sem_t
*sem
, char *what
)
271 FSHOW((stderr
, "%s: os_sem_wait(%p)\n", what
, sem
));
272 ret
= semaphore_wait(*sem
);
273 FSHOW((stderr
, "%s: os_sem_wait(%p) => %s\n", what
, sem
,
274 KERN_SUCCESS
==ret
? "ok" : strerror(errno
)));
278 /* It is unclear just when we can get this, but a sufficiently
279 * long wait seems to do that, at least sometimes.
281 * However, a wait that long is definitely abnormal for the
282 * GC, so we complain before retrying.
284 case KERN_OPERATION_TIMED_OUT
:
285 fprintf(stderr
, "%s: os_sem_wait(%p): %s", what
, sem
, strerror(errno
));
286 /* This is analogous to POSIX EINTR. */
290 lose("%s: os_sem_wait(%p): %lu, %s", what
, sem
, ret
, strerror(errno
));
295 os_sem_post(os_sem_t
*sem
, char *what
)
297 if (KERN_SUCCESS
!=semaphore_signal(*sem
))
298 lose("%s: os_sem_post(%p): %s", what
, sem
, strerror(errno
));
299 FSHOW((stderr
, "%s: os_sem_post(%p) ok\n", what
, sem
));
303 os_sem_destroy(os_sem_t
*sem
)
305 if (-1==semaphore_destroy(current_mach_task
, *sem
))
306 lose("os_sem_destroy(%p): %s", sem
, strerror(errno
));
311 #if defined(LISP_FEATURE_SB_WTIMER)
313 # error Completely untested. Go ahead! Remove this line, try your luck!
316 * Waitable timer implementation for the safepoint-based (SIGALRM-free)
317 * timer facility using kqueue.
319 * Unlike FreeBSD with its ms (!) timer resolution, Darwin supports ns
320 * timer resolution -- or at least it pretends to do so on the API
321 * level (?). To use it, we need the *64 versions of the functions and
324 * Unfortunately, I don't run Darwin, and can't test this code, so it's
325 * just a hopeful translation from FreeBSD.
333 lose("os_create_wtimer: kqueue");
338 os_wait_for_wtimer(int kq
)
340 struct kevent64_s ev
;
342 if ( (n
= kevent64(kq
, 0, 0, &ev
, 1, 0, 0)) == -1) {
344 lose("os_wtimer_listen failed");
351 os_close_wtimer(int kq
)
354 lose("os_close_wtimer failed");
358 os_set_wtimer(int kq
, int sec
, int nsec
)
360 int64_t nsec
= ((int64_t) sec
) * 1000000000 + (int64_t) nsec
;
362 struct kevent64_s ev
;
363 EV_SET64(&ev
, 1, EVFILT_TIMER
, EV_ADD
|EV_ENABLE
|EV_ONESHOT
, NOTE_NSECONDS
,
365 if (kevent64(kq
, &ev
, 1, 0, 0, 0, 0) == -1)
366 perror("os_set_wtimer: kevent");
370 os_cancel_wtimer(int kq
)
372 struct kevent64_s ev
;
373 EV_SET64(&ev
, 1, EVFILT_TIMER
, EV_DISABLE
, 0, 0, 0, 0, 0);
374 if (kevent64(kq
, &ev
, 1, 0, 0, 0, 0) == -1 && errno
!= ENOENT
)
375 perror("os_cancel_wtimer: kevent");