1 /*
2  * This is the Darwin incarnation of OS-dependent routines. See also
3  * "bsd-os.c".
4  */
5 
6 /*
7  * This software is part of the SBCL system. See the README file for
8  * more information.
9  *
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.
15  */
16 
17 #include "thread.h"
18 #include "sbcl.h"
19 #include "globals.h"
20 #include "runtime.h"
21 #include <signal.h>
22 #include <limits.h>
23 #include <mach-o/dyld.h>
24 #include <stdio.h>
25 #include <errno.h>
26 #include <dlfcn.h>
27 #include <pthread.h>
28 #include <mach/mach.h>
29 #include <mach/clock.h>
30 #include <stdlib.h>
31 #include <time.h>
32 #include <sys/syscall.h>
33 
34 #ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
35 #include <libkern/OSAtomic.h>
36 #endif
37 
38 #if defined(LISP_FEATURE_SB_WTIMER)
39 # include <sys/types.h>
40 # include <sys/event.h>
41 # include <sys/time.h>
42 #endif
43 
44 char *
os_get_runtime_executable_path(int external)45 os_get_runtime_executable_path(int external)
46 {
47     char path[PATH_MAX + 1];
48     uint32_t size = sizeof(path);
49 
50     if (_NSGetExecutablePath(path, &size) == -1)
51         return NULL;
52 
53     return copied_string(path);
54 }
55 
56 
57 semaphore_t clock_sem = MACH_PORT_NULL;
58 mach_port_t clock_port = MACH_PORT_NULL;
59 
init_mach_clock()60 void init_mach_clock() {
61     if (host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &clock_port)
62         != KERN_SUCCESS) {
63         lose("Error initializing clocks");
64     }
65 
66     if (semaphore_create(mach_task_self_, &clock_sem, SYNC_POLICY_FIFO, 0)
67         != KERN_SUCCESS) {
68         lose("Error initializing clocks");
69     }
70 }
71 
72 #ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
73 
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.
77  *
78  */
79 extern boolean_t exc_server();
80 
81 void *
mach_exception_handler(void * port)82 mach_exception_handler(void *port)
83 {
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
87    */
88   lose("mach_msg_server returned");
89 }
90 
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. */
95 
96 mach_port_t mach_exception_handler_port_set = MACH_PORT_NULL;
97 
98 pthread_t
setup_mach_exception_handling_thread()99 setup_mach_exception_handling_thread()
100 {
101     kern_return_t ret;
102     pthread_t mach_exception_handling_thread = NULL;
103     pthread_attr_t attr;
104 
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);
109 
110     /* create the thread that will receive the mach exceptions */
111 
112     FSHOW((stderr, "Creating mach_exception_handler thread!\n"));
113 
114     pthread_attr_init(&attr);
115     pthread_create(&mach_exception_handling_thread,
116                    &attr,
117                    mach_exception_handler,
118                    (void*)(long)mach_exception_handler_port_set);
119     pthread_attr_destroy(&attr);
120 
121     return mach_exception_handling_thread;
122 }
123 
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). */
127 kern_return_t
mach_lisp_thread_init(struct thread * thread)128 mach_lisp_thread_init(struct thread * thread)
129 {
130     kern_return_t ret;
131     mach_port_t current_mach_thread, thread_exception_port;
132 
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");
137     }
138 
139     if (mach_port_set_context(mach_task_self(), thread_exception_port,
140                               (mach_vm_address_t)thread)
141         != KERN_SUCCESS) {
142         lose("Cannot set thread_exception_port context");
143     }
144     thread->mach_port_name = thread_exception_port;
145 
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);
151     if (ret) {
152         lose("mach_port_insert_right failed with return_code %d\n", ret);
153     }
154 
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,
159                                      EXCEPTION_DEFAULT,
160                                      THREAD_STATE_NONE);
161     if (ret) {
162         lose("thread_set_exception_ports failed with return_code %d\n", ret);
163     }
164 
165     ret = mach_port_deallocate (mach_task_self(), current_mach_thread);
166     if (ret) {
167         lose("mach_port_deallocate failed with return_code %d\n", ret);
168     }
169 
170     ret = mach_port_move_member(mach_task_self(),
171                                 thread_exception_port,
172                                 mach_exception_handler_port_set);
173     if (ret) {
174         lose("mach_port_move_member failed with return_code %d\n", ret);
175     }
176 
177     return ret;
178 }
179 
180 void
mach_lisp_thread_destroy(struct thread * thread)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)
185         != KERN_SUCCESS) {
186         lose("Error destroying an exception port");
187     }
188     if (mach_port_deallocate(mach_task_self(), port) != KERN_SUCCESS) {
189         lose("Error destroying an exception port");
190     }
191 
192     if (mach_port_destroy(mach_task_self(), port) != KERN_SUCCESS) {
193         lose("Error destroying an exception port");
194     }
195 }
196 
197 void
setup_mach_exceptions()198 setup_mach_exceptions() {
199     setup_mach_exception_handling_thread();
200     mach_lisp_thread_init(all_threads);
201 }
202 
203 pid_t
mach_fork()204 mach_fork() {
205     pid_t pid = fork();
206     if (pid == 0) {
207         setup_mach_exceptions();
208         return pid;
209     } else {
210         return pid;
211     }
212 }
213 #endif
214 
darwin_init(void)215 void darwin_init(void)
216 {
217     init_mach_clock();
218 #ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
219     setup_mach_exception_handling_thread();
220 #endif
221 }
222 
223 
224 #ifdef LISP_FEATURE_SB_THREAD
225 
226 inline void
os_sem_init(os_sem_t * sem,unsigned int value)227 os_sem_init(os_sem_t *sem, unsigned int value)
228 {
229     if (KERN_SUCCESS!=semaphore_create(mach_task_self(), sem, SYNC_POLICY_FIFO, (int)value))
230         lose("os_sem_init(%p): %s", sem, strerror(errno));
231 }
232 
233 inline void
os_sem_wait(os_sem_t * sem,char * what)234 os_sem_wait(os_sem_t *sem, char *what)
235 {
236     kern_return_t ret;
237   restart:
238     FSHOW((stderr, "%s: os_sem_wait(%p)\n", what, sem));
239     ret = semaphore_wait(*sem);
240     FSHOW((stderr, "%s: os_sem_wait(%p) => %s\n", what, sem,
241            KERN_SUCCESS==ret ? "ok" : strerror(errno)));
242     switch (ret) {
243     case KERN_SUCCESS:
244         return;
245         /* It is unclear just when we can get this, but a sufficiently
246          * long wait seems to do that, at least sometimes.
247          *
248          * However, a wait that long is definitely abnormal for the
249          * GC, so we complain before retrying.
250          */
251     case KERN_OPERATION_TIMED_OUT:
252         fprintf(stderr, "%s: os_sem_wait(%p): %s", what, sem, strerror(errno));
253         /* This is analogous to POSIX EINTR. */
254     case KERN_ABORTED:
255         goto restart;
256     default:
257         lose("%s: os_sem_wait(%p): %lu, %s", what, sem, ret, strerror(errno));
258     }
259 }
260 
261 void
os_sem_post(os_sem_t * sem,char * what)262 os_sem_post(os_sem_t *sem, char *what)
263 {
264     if (KERN_SUCCESS!=semaphore_signal(*sem))
265         lose("%s: os_sem_post(%p): %s", what, sem, strerror(errno));
266     FSHOW((stderr, "%s: os_sem_post(%p) ok\n", what, sem));
267 }
268 
269 void
os_sem_destroy(os_sem_t * sem)270 os_sem_destroy(os_sem_t *sem)
271 {
272     if (-1==semaphore_destroy(mach_task_self(), *sem))
273         lose("os_sem_destroy(%p): %s", sem, strerror(errno));
274 }
275 
276 #endif
277 
278 #if defined(LISP_FEATURE_SB_WTIMER)
279 
280 # error Completely untested. Go ahead! Remove this line, try your luck!
281 
282 /*
283  * Waitable timer implementation for the safepoint-based (SIGALRM-free)
284  * timer facility using kqueue.
285  *
286  * Unlike FreeBSD with its ms (!) timer resolution, Darwin supports ns
287  * timer resolution -- or at least it pretends to do so on the API
288  * level (?).  To use it, we need the *64 versions of the functions and
289  * structures.
290  *
291  * Unfortunately, I don't run Darwin, and can't test this code, so it's
292  * just a hopeful translation from FreeBSD.
293  */
294 
295 int
os_create_wtimer()296 os_create_wtimer()
297 {
298     int kq = kqueue();
299     if (kq == -1)
300         lose("os_create_wtimer: kqueue");
301     return kq;
302 }
303 
304 int
os_wait_for_wtimer(int kq)305 os_wait_for_wtimer(int kq)
306 {
307     struct kevent64_s ev;
308     int n;
309     if ( (n = kevent64(kq, 0, 0, &ev, 1, 0, 0)) == -1) {
310         if (errno != EINTR)
311             lose("os_wtimer_listen failed");
312         n = 0;
313     }
314     return n != 1;
315 }
316 
317 void
os_close_wtimer(int kq)318 os_close_wtimer(int kq)
319 {
320     if (close(kq) == -1)
321         lose("os_close_wtimer failed");
322 }
323 
324 void
os_set_wtimer(int kq,int sec,int nsec)325 os_set_wtimer(int kq, int sec, int nsec)
326 {
327     int64_t nsec = ((int64_t) sec) * 1000000000 + (int64_t) nsec;
328 
329     struct kevent64_s ev;
330     EV_SET64(&ev, 1, EVFILT_TIMER, EV_ADD|EV_ENABLE|EV_ONESHOT, NOTE_NSECONDS,
331              nsec, 0, 0, 0);
332     if (kevent64(kq, &ev, 1, 0, 0, 0, 0) == -1)
333         perror("os_set_wtimer: kevent");
334 }
335 
336 void
os_cancel_wtimer(int kq)337 os_cancel_wtimer(int kq)
338 {
339     struct kevent64_s ev;
340     EV_SET64(&ev, 1, EVFILT_TIMER, EV_DISABLE, 0, 0, 0, 0, 0);
341     if (kevent64(kq, &ev, 1, 0, 0, 0, 0) == -1 && errno != ENOENT)
342         perror("os_cancel_wtimer: kevent");
343 }
344 #endif
345 
346 /* nanosleep() is not re-entrant on some versions of Darwin,
347  * reimplement it using the underlying syscalls. */
348 int
sb_nanosleep(time_t sec,int nsec)349 sb_nanosleep(time_t sec, int nsec) {
350     int ret;
351     mach_timespec_t current_time;
352     mach_timespec_t start_time;
353 
354     if (sec < 0 || nsec >= NSEC_PER_SEC) {
355         errno = EINVAL;
356         return -1;
357     }
358 
359     ret = clock_get_time(clock_port, &start_time);
360     if (ret != KERN_SUCCESS) {
361             lose(mach_error_string(ret));
362     }
363 
364     for (;;) {
365 
366       /* Older version do not have a wrapper. */
367       ret = syscall(SYS___semwait_signal, (int)clock_sem, (int)MACH_PORT_NULL, (int)1, (int)1,
368                     (__int64_t)sec, (__int32_t)nsec);
369         if (ret < 0) {
370             if (errno == ETIMEDOUT) {
371                 return 0;
372             }
373             if (errno == EINTR) {
374                 ret = clock_get_time(clock_port, &current_time);
375                 if (ret != KERN_SUCCESS) {
376                     lose(mach_error_string(ret));
377                 }
378                 time_t elapsed_sec = current_time.tv_sec - start_time.tv_sec;
379                 int elapsed_nsec = current_time.tv_nsec - start_time.tv_nsec;
380                 if (elapsed_nsec < 0) {
381                     elapsed_sec--;
382                     elapsed_nsec += NSEC_PER_SEC;
383                 }
384                 sec -= elapsed_sec;
385                 nsec -= elapsed_nsec;
386                 if (nsec < 0) {
387                     sec--;
388                     nsec += NSEC_PER_SEC;
389                 }
390                 if (sec < 0 || (sec == 0 && nsec == 0)) {
391                     return 0;
392                 }
393                 start_time = current_time;
394             } else {
395                 errno = EINVAL;
396                 return -1;
397             }
398         } else {
399             return -1;
400         }
401     }
402 }
403