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 #include <mach/mach.h>
29 #include <mach/clock.h>
32 #include <sys/syscall.h>
34 #ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
35 #include <libkern/OSAtomic.h>
38 #if defined(LISP_FEATURE_SB_WTIMER)
39 # include <sys/types.h>
40 # include <sys/event.h>
41 # include <sys/time.h>
45 os_get_runtime_executable_path(int external
)
47 char path
[PATH_MAX
+ 1];
48 uint32_t size
= sizeof(path
);
50 if (_NSGetExecutablePath(path
, &size
) == -1)
53 return copied_string(path
);
57 semaphore_t clock_sem
= MACH_PORT_NULL
;
58 mach_port_t clock_port
= MACH_PORT_NULL
;
60 void init_mach_clock() {
61 if (host_get_clock_service(mach_host_self(), SYSTEM_CLOCK
, &clock_port
)
63 lose("Error initializing clocks");
66 if (semaphore_create(mach_task_self_
, &clock_sem
, SYNC_POLICY_FIFO
, 0)
68 lose("Error initializing clocks");
72 #ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
74 /* exc_server handles mach exception messages from the kernel and
75 * calls catch exception raise. We use the system-provided
76 * mach_msg_server, which, I assume, calls exc_server in a loop.
79 extern boolean_t
exc_server();
82 mach_exception_handler(void *port
)
84 mach_msg_server(exc_server
, 2048, (mach_port_t
) port
, 0);
85 /* mach_msg_server should never return, but it should dispatch mach
86 * exceptions to our catch_exception_raise function
88 lose("mach_msg_server returned");
91 /* Sets up the thread that will listen for mach exceptions. note that
92 the exception handlers will be run on this thread. This is
93 different from the BSD-style signal handling situation in which the
94 signal handlers run in the relevant thread directly. */
96 mach_port_t mach_exception_handler_port_set
= MACH_PORT_NULL
;
99 setup_mach_exception_handling_thread()
102 pthread_t mach_exception_handling_thread
= NULL
;
105 /* allocate a mach_port for this process */
106 ret
= mach_port_allocate(mach_task_self(),
107 MACH_PORT_RIGHT_PORT_SET
,
108 &mach_exception_handler_port_set
);
110 /* create the thread that will receive the mach exceptions */
112 FSHOW((stderr
, "Creating mach_exception_handler thread!\n"));
114 pthread_attr_init(&attr
);
115 pthread_create(&mach_exception_handling_thread
,
117 mach_exception_handler
,
118 (void*)(long)mach_exception_handler_port_set
);
119 pthread_attr_destroy(&attr
);
121 return mach_exception_handling_thread
;
124 /* tell the kernel that we want EXC_BAD_ACCESS exceptions sent to the
125 exception port (which is being listened to do by the mach
126 exception handling thread). */
128 mach_lisp_thread_init(struct thread
* thread
)
131 mach_port_t current_mach_thread
, thread_exception_port
;
133 if (mach_port_allocate(mach_task_self(),
134 MACH_PORT_RIGHT_RECEIVE
,
135 &thread_exception_port
) != KERN_SUCCESS
) {
136 lose("Cannot allocate thread_exception_port");
139 if (mach_port_set_context(mach_task_self(), thread_exception_port
,
140 (mach_vm_address_t
)thread
)
142 lose("Cannot set thread_exception_port context");
144 thread
->mach_port_name
= thread_exception_port
;
146 /* establish the right for the thread_exception_port to send messages */
147 ret
= mach_port_insert_right(mach_task_self(),
148 thread_exception_port
,
149 thread_exception_port
,
150 MACH_MSG_TYPE_MAKE_SEND
);
152 lose("mach_port_insert_right failed with return_code %d\n", ret
);
155 current_mach_thread
= mach_thread_self();
156 ret
= thread_set_exception_ports(current_mach_thread
,
157 EXC_MASK_BAD_ACCESS
| EXC_MASK_BAD_INSTRUCTION
,
158 thread_exception_port
,
162 lose("thread_set_exception_ports failed with return_code %d\n", ret
);
165 ret
= mach_port_deallocate (mach_task_self(), current_mach_thread
);
167 lose("mach_port_deallocate failed with return_code %d\n", ret
);
170 ret
= mach_port_move_member(mach_task_self(),
171 thread_exception_port
,
172 mach_exception_handler_port_set
);
174 lose("mach_port_move_member failed with return_code %d\n", ret
);
181 mach_lisp_thread_destroy(struct thread
*thread
) {
182 mach_port_t port
= thread
->mach_port_name
;
183 FSHOW((stderr
, "Deallocating mach port %x\n", port
));
184 if (mach_port_move_member(mach_task_self(), port
, MACH_PORT_NULL
)
186 lose("Error destroying an exception port");
188 if (mach_port_deallocate(mach_task_self(), port
) != KERN_SUCCESS
) {
189 lose("Error destroying an exception port");
192 if (mach_port_destroy(mach_task_self(), port
) != KERN_SUCCESS
) {
193 lose("Error destroying an exception port");
201 #ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
202 setup_mach_exception_handling_thread();
203 mach_lisp_thread_init(all_threads
);
207 void darwin_init(void)
210 #ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
211 setup_mach_exception_handling_thread();
216 #ifdef LISP_FEATURE_SB_THREAD
219 os_sem_init(os_sem_t
*sem
, unsigned int value
)
221 if (KERN_SUCCESS
!=semaphore_create(mach_task_self(), sem
, SYNC_POLICY_FIFO
, (int)value
))
222 lose("os_sem_init(%p): %s", sem
, strerror(errno
));
226 os_sem_wait(os_sem_t
*sem
, char *what
)
230 FSHOW((stderr
, "%s: os_sem_wait(%p)\n", what
, sem
));
231 ret
= semaphore_wait(*sem
);
232 FSHOW((stderr
, "%s: os_sem_wait(%p) => %s\n", what
, sem
,
233 KERN_SUCCESS
==ret
? "ok" : strerror(errno
)));
237 /* It is unclear just when we can get this, but a sufficiently
238 * long wait seems to do that, at least sometimes.
240 * However, a wait that long is definitely abnormal for the
241 * GC, so we complain before retrying.
243 case KERN_OPERATION_TIMED_OUT
:
244 fprintf(stderr
, "%s: os_sem_wait(%p): %s", what
, sem
, strerror(errno
));
245 /* This is analogous to POSIX EINTR. */
249 lose("%s: os_sem_wait(%p): %lu, %s", what
, sem
, ret
, strerror(errno
));
254 os_sem_post(os_sem_t
*sem
, char *what
)
256 if (KERN_SUCCESS
!=semaphore_signal(*sem
))
257 lose("%s: os_sem_post(%p): %s", what
, sem
, strerror(errno
));
258 FSHOW((stderr
, "%s: os_sem_post(%p) ok\n", what
, sem
));
262 os_sem_destroy(os_sem_t
*sem
)
264 if (-1==semaphore_destroy(mach_task_self(), *sem
))
265 lose("os_sem_destroy(%p): %s", sem
, strerror(errno
));
270 #if defined(LISP_FEATURE_SB_WTIMER)
272 # error Completely untested. Go ahead! Remove this line, try your luck!
275 * Waitable timer implementation for the safepoint-based (SIGALRM-free)
276 * timer facility using kqueue.
278 * Unlike FreeBSD with its ms (!) timer resolution, Darwin supports ns
279 * timer resolution -- or at least it pretends to do so on the API
280 * level (?). To use it, we need the *64 versions of the functions and
283 * Unfortunately, I don't run Darwin, and can't test this code, so it's
284 * just a hopeful translation from FreeBSD.
292 lose("os_create_wtimer: kqueue");
297 os_wait_for_wtimer(int kq
)
299 struct kevent64_s ev
;
301 if ( (n
= kevent64(kq
, 0, 0, &ev
, 1, 0, 0)) == -1) {
303 lose("os_wtimer_listen failed");
310 os_close_wtimer(int kq
)
313 lose("os_close_wtimer failed");
317 os_set_wtimer(int kq
, int sec
, int nsec
)
319 int64_t nsec
= ((int64_t) sec
) * 1000000000 + (int64_t) nsec
;
321 struct kevent64_s ev
;
322 EV_SET64(&ev
, 1, EVFILT_TIMER
, EV_ADD
|EV_ENABLE
|EV_ONESHOT
, NOTE_NSECONDS
,
324 if (kevent64(kq
, &ev
, 1, 0, 0, 0, 0) == -1)
325 perror("os_set_wtimer: kevent");
329 os_cancel_wtimer(int kq
)
331 struct kevent64_s ev
;
332 EV_SET64(&ev
, 1, EVFILT_TIMER
, EV_DISABLE
, 0, 0, 0, 0, 0);
333 if (kevent64(kq
, &ev
, 1, 0, 0, 0, 0) == -1 && errno
!= ENOENT
)
334 perror("os_cancel_wtimer: kevent");
338 /* nanosleep() is not re-entrant on some versions of Darwin,
339 * reimplement it using the underlying syscalls. */
341 sb_nanosleep(time_t sec
, int nsec
) {
343 mach_timespec_t current_time
;
344 mach_timespec_t start_time
;
346 if (sec
< 0 || nsec
>= NSEC_PER_SEC
) {
351 ret
= clock_get_time(clock_port
, &start_time
);
352 if (ret
!= KERN_SUCCESS
) {
353 lose(mach_error_string(ret
));
358 /* Older version do not have a wrapper. */
359 ret
= syscall(SYS___semwait_signal
, (int)clock_sem
, (int)MACH_PORT_NULL
, (int)1, (int)1,
360 (__int64_t
)sec
, (__int32_t
)nsec
);
362 if (errno
== ETIMEDOUT
) {
365 if (errno
== EINTR
) {
366 ret
= clock_get_time(clock_port
, ¤t_time
);
367 if (ret
!= KERN_SUCCESS
) {
368 lose(mach_error_string(ret
));
370 time_t elapsed_sec
= current_time
.tv_sec
- start_time
.tv_sec
;
371 int elapsed_nsec
= current_time
.tv_nsec
- start_time
.tv_nsec
;
372 if (elapsed_nsec
< 0) {
374 elapsed_nsec
+= NSEC_PER_SEC
;
377 nsec
-= elapsed_nsec
;
380 nsec
+= NSEC_PER_SEC
;
382 if (sec
< 0 || (sec
== 0 && nsec
== 0)) {
385 start_time
= current_time
;