Add a safepoint-based mechanism to avoid SIGALRM for the TIMER facility
[sbcl.git] / src / runtime / darwin-os.c
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
28 #ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
29 #include <mach/mach.h>
30 #include <libkern/OSAtomic.h>
31 #include <stdlib.h>
32 #endif
33
34 #if defined(LISP_FEATURE_SB_WTIMER)
35 # include <sys/types.h>
36 # include <sys/event.h>
37 # include <sys/time.h>
38 #endif
39
40 char *
41 os_get_runtime_executable_path(int external)
42 {
43     char path[PATH_MAX + 1];
44     uint32_t size = sizeof(path);
45
46     if (_NSGetExecutablePath(path, &size) == -1)
47         return NULL;
48
49     return copied_string(path);
50 }
51
52 #ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
53
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.
57  *
58  */
59 extern boolean_t exc_server();
60
61 void *
62 mach_exception_handler(void *port)
63 {
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
67    */
68   lose("mach_msg_server returned");
69 }
70
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. */
75
76 mach_port_t mach_exception_handler_port_set = MACH_PORT_NULL;
77 mach_port_t current_mach_task = MACH_PORT_NULL;
78
79 pthread_t
80 setup_mach_exception_handling_thread()
81 {
82     kern_return_t ret;
83     pthread_t mach_exception_handling_thread = NULL;
84     pthread_attr_t attr;
85
86     current_mach_task = mach_task_self();
87
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);
92
93     /* create the thread that will receive the mach exceptions */
94
95     FSHOW((stderr, "Creating mach_exception_handler thread!\n"));
96
97     pthread_attr_init(&attr);
98     pthread_create(&mach_exception_handling_thread,
99                    &attr,
100                    mach_exception_handler,
101                    (void*) mach_exception_handler_port_set);
102     pthread_attr_destroy(&attr);
103
104     return mach_exception_handling_thread;
105 }
106
107 struct exception_port_record
108 {
109     struct thread * thread;
110     struct exception_port_record * next;
111 };
112
113 static OSQueueHead free_records = OS_ATOMIC_QUEUE_INIT;
114
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
118  * as a port name.
119  *
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
122  * thread.
123  *
124  * The lock-free free-list above is used as a cheap fast path.
125  */
126 static mach_port_t
127 find_receive_port(struct thread * thread)
128 {
129     mach_port_t ret;
130     struct exception_port_record * curr, * to_free = NULL;
131     unsigned long i;
132     for (i = 1;; i++) {
133         curr = OSAtomicDequeue(&free_records, offsetof(struct exception_port_record, next));
134         if (curr == NULL) {
135             curr = calloc(1, sizeof(struct exception_port_record));
136             if (curr == NULL)
137                 lose("unable to allocate exception_port_record\n");
138         }
139 #ifdef LISP_FEATURE_X86_64
140         if ((mach_port_t)curr != (unsigned long)curr)
141             goto skip;
142 #endif
143
144         if (mach_port_allocate_name(current_mach_task,
145                                     MACH_PORT_RIGHT_RECEIVE,
146                                     (mach_port_t)curr))
147             goto skip;
148         curr->thread = thread;
149         ret = (mach_port_t)curr;
150         break;
151         skip:
152         curr->next = to_free;
153         to_free = curr;
154         if ((i % 1024) == 0)
155             FSHOW((stderr, "Looped %lu times trying to allocate an exception port\n"));
156     }
157     while (to_free != NULL) {
158         struct exception_port_record * current = to_free;
159         to_free = to_free->next;
160         free(current);
161     }
162
163     FSHOW((stderr, "Allocated exception port %x for thread %p\n", ret, thread));
164
165     return ret;
166 }
167
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). */
171 kern_return_t
172 mach_lisp_thread_init(struct thread * thread)
173 {
174     kern_return_t ret;
175     mach_port_t current_mach_thread, thread_exception_port;
176
177     /* allocate a named port for the thread */
178     thread_exception_port
179         = thread->mach_port_name
180         = find_receive_port(thread);
181
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);
187     if (ret) {
188         lose("mach_port_insert_right failed with return_code %d\n", ret);
189     }
190
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,
195                                      EXCEPTION_DEFAULT,
196                                      THREAD_STATE_NONE);
197     if (ret) {
198         lose("thread_set_exception_ports failed with return_code %d\n", ret);
199     }
200
201     ret = mach_port_deallocate (current_mach_task, current_mach_thread);
202     if (ret) {
203         lose("mach_port_deallocate failed with return_code %d\n", ret);
204     }
205
206     ret = mach_port_move_member(current_mach_task,
207                                 thread_exception_port,
208                                 mach_exception_handler_port_set);
209     if (ret) {
210         lose("mach_port_move_member failed with return_code %d\n", ret);
211     }
212
213     return ret;
214 }
215
216 kern_return_t
217 mach_lisp_thread_destroy(struct thread *thread) {
218     kern_return_t ret;
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);
223
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));
227
228     return ret;
229 }
230
231 void
232 setup_mach_exceptions() {
233     setup_mach_exception_handling_thread();
234     mach_lisp_thread_init(all_threads);
235 }
236
237 pid_t
238 mach_fork() {
239     pid_t pid = fork();
240     if (pid == 0) {
241         setup_mach_exceptions();
242         return pid;
243     } else {
244         return pid;
245     }
246 }
247 #endif
248
249 void darwin_init(void)
250 {
251 #ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
252     setup_mach_exception_handling_thread();
253 #endif
254 }
255
256
257 #ifdef LISP_FEATURE_SB_THREAD
258
259 inline void
260 os_sem_init(os_sem_t *sem, unsigned int value)
261 {
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));
264 }
265
266 inline void
267 os_sem_wait(os_sem_t *sem, char *what)
268 {
269     kern_return_t ret;
270   restart:
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)));
275     switch (ret) {
276     case KERN_SUCCESS:
277         return;
278         /* It is unclear just when we can get this, but a sufficiently
279          * long wait seems to do that, at least sometimes.
280          *
281          * However, a wait that long is definitely abnormal for the
282          * GC, so we complain before retrying.
283          */
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. */
287     case KERN_ABORTED:
288         goto restart;
289     default:
290         lose("%s: os_sem_wait(%p): %lu, %s", what, sem, ret, strerror(errno));
291     }
292 }
293
294 void
295 os_sem_post(os_sem_t *sem, char *what)
296 {
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));
300 }
301
302 void
303 os_sem_destroy(os_sem_t *sem)
304 {
305     if (-1==semaphore_destroy(current_mach_task, *sem))
306         lose("os_sem_destroy(%p): %s", sem, strerror(errno));
307 }
308
309 #endif
310
311 #if defined(LISP_FEATURE_SB_WTIMER)
312
313 # error Completely untested. Go ahead! Remove this line, try your luck!
314
315 /*
316  * Waitable timer implementation for the safepoint-based (SIGALRM-free)
317  * timer facility using kqueue.
318  *
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
322  * structures.
323  *
324  * Unfortunately, I don't run Darwin, and can't test this code, so it's
325  * just a hopeful translation from FreeBSD.
326  */
327
328 int
329 os_create_wtimer()
330 {
331     int kq = kqueue();
332     if (kq == -1)
333         lose("os_create_wtimer: kqueue");
334     return kq;
335 }
336
337 int
338 os_wait_for_wtimer(int kq)
339 {
340     struct kevent64_s ev;
341     int n;
342     if ( (n = kevent64(kq, 0, 0, &ev, 1, 0, 0)) == -1) {
343         if (errno != EINTR)
344             lose("os_wtimer_listen failed");
345         n = 0;
346     }
347     return n != 1;
348 }
349
350 void
351 os_close_wtimer(int kq)
352 {
353     if (close(kq) == -1)
354         lose("os_close_wtimer failed");
355 }
356
357 void
358 os_set_wtimer(int kq, int sec, int nsec)
359 {
360     int64_t nsec = ((int64_t) sec) * 1000000000 + (int64_t) nsec;
361
362     struct kevent64_s ev;
363     EV_SET64(&ev, 1, EVFILT_TIMER, EV_ADD|EV_ENABLE|EV_ONESHOT, NOTE_NSECONDS,
364              nsec, 0, 0, 0);
365     if (kevent64(kq, &ev, 1, 0, 0, 0, 0) == -1)
366         perror("os_set_wtimer: kevent");
367 }
368
369 void
370 os_cancel_wtimer(int kq)
371 {
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");
376 }
377 #endif