fbbae266e20a2cd7099e62a21200290741f6a9a7
[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 #endif
31
32 char *
33 os_get_runtime_executable_path(int external)
34 {
35     char path[PATH_MAX + 1];
36     uint32_t size = sizeof(path);
37
38     if (_NSGetExecutablePath(path, &size) == -1)
39         return NULL;
40
41     return copied_string(path);
42 }
43
44 #ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
45
46 /* exc_server handles mach exception messages from the kernel and
47  * calls catch exception raise. We use the system-provided
48  * mach_msg_server, which, I assume, calls exc_server in a loop.
49  *
50  */
51 extern boolean_t exc_server();
52
53 void *
54 mach_exception_handler(void *port)
55 {
56   mach_msg_server(exc_server, 2048, (mach_port_t) port, 0);
57   /* mach_msg_server should never return, but it should dispatch mach
58    * exceptions to our catch_exception_raise function
59    */
60   lose("mach_msg_server returned");
61 }
62
63 /* Sets up the thread that will listen for mach exceptions. note that
64    the exception handlers will be run on this thread. This is
65    different from the BSD-style signal handling situation in which the
66    signal handlers run in the relevant thread directly. */
67
68 mach_port_t mach_exception_handler_port_set = MACH_PORT_NULL;
69 mach_port_t current_mach_task = MACH_PORT_NULL;
70
71 pthread_t
72 setup_mach_exception_handling_thread()
73 {
74     kern_return_t ret;
75     pthread_t mach_exception_handling_thread = NULL;
76     pthread_attr_t attr;
77
78     current_mach_task = mach_task_self();
79
80     /* allocate a mach_port for this process */
81     ret = mach_port_allocate(current_mach_task,
82                              MACH_PORT_RIGHT_PORT_SET,
83                              &mach_exception_handler_port_set);
84
85     /* create the thread that will receive the mach exceptions */
86
87     FSHOW((stderr, "Creating mach_exception_handler thread!\n"));
88
89     pthread_attr_init(&attr);
90     pthread_create(&mach_exception_handling_thread,
91                    &attr,
92                    mach_exception_handler,
93                    (void*) mach_exception_handler_port_set);
94     pthread_attr_destroy(&attr);
95
96     return mach_exception_handling_thread;
97 }
98
99 /* tell the kernel that we want EXC_BAD_ACCESS exceptions sent to the
100    exception port (which is being listened to do by the mach
101    exception handling thread). */
102 kern_return_t
103 mach_thread_init(mach_port_t thread_exception_port)
104 {
105     kern_return_t ret;
106     mach_port_t current_mach_thread;
107
108     /* allocate a named port for the thread */
109     FSHOW((stderr, "Allocating mach port %x\n", thread_exception_port));
110     ret = mach_port_allocate_name(current_mach_task,
111                                   MACH_PORT_RIGHT_RECEIVE,
112                                   thread_exception_port);
113     if (ret) {
114         lose("mach_port_allocate_name failed with return_code %d\n", ret);
115     }
116
117     /* establish the right for the thread_exception_port to send messages */
118     ret = mach_port_insert_right(current_mach_task,
119                                  thread_exception_port,
120                                  thread_exception_port,
121                                  MACH_MSG_TYPE_MAKE_SEND);
122     if (ret) {
123         lose("mach_port_insert_right failed with return_code %d\n", ret);
124     }
125
126     current_mach_thread = mach_thread_self();
127     ret = thread_set_exception_ports(current_mach_thread,
128                                      EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION,
129                                      thread_exception_port,
130                                      EXCEPTION_DEFAULT,
131                                      THREAD_STATE_NONE);
132     if (ret) {
133         lose("thread_set_exception_ports failed with return_code %d\n", ret);
134     }
135
136     ret = mach_port_deallocate (current_mach_task, current_mach_thread);
137     if (ret) {
138         lose("mach_port_deallocate failed with return_code %d\n", ret);
139     }
140
141     ret = mach_port_move_member(current_mach_task,
142                                 thread_exception_port,
143                                 mach_exception_handler_port_set);
144     if (ret) {
145         lose("mach_port_move_member failed with return_code %d\n", ret);
146     }
147
148     return ret;
149 }
150
151 void
152 setup_mach_exceptions() {
153     setup_mach_exception_handling_thread();
154     mach_thread_init(THREAD_STRUCT_TO_EXCEPTION_PORT(all_threads));
155 }
156
157 pid_t
158 mach_fork() {
159     pid_t pid = fork();
160     if (pid == 0) {
161         setup_mach_exceptions();
162         return pid;
163     } else {
164         return pid;
165     }
166 }
167 #endif
168
169 void darwin_init(void)
170 {
171 #ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
172     setup_mach_exception_handling_thread();
173 #endif
174 }
175
176
177 #ifdef LISP_FEATURE_SB_THREAD
178
179 inline void
180 os_sem_init(os_sem_t *sem, unsigned int value)
181 {
182     if (KERN_SUCCESS!=semaphore_create(current_mach_task, sem, SYNC_POLICY_FIFO, (int)value))
183         lose("os_sem_init(%p): %s", sem, strerror(errno));
184 }
185
186 inline void
187 os_sem_wait(os_sem_t *sem, char *what)
188 {
189     kern_return_t ret;
190   restart:
191     FSHOW((stderr, "%s: os_sem_wait(%p)\n", what, sem));
192     ret = semaphore_wait(*sem);
193     FSHOW((stderr, "%s: os_sem_wait(%p) => %s\n", what, sem,
194            KERN_SUCCESS==ret ? "ok" : strerror(errno)));
195     switch (ret) {
196     case KERN_SUCCESS:
197         return;
198         /* It is unclear just when we can get this, but a sufficiently
199          * long wait seems to do that, at least sometimes.
200          *
201          * However, a wait that long is definitely abnormal for the
202          * GC, so we complain before retrying.
203          */
204     case KERN_OPERATION_TIMED_OUT:
205         fprintf(stderr, "%s: os_sem_wait(%p): %s", what, sem, strerror(errno));
206         /* This is analogous to POSIX EINTR. */
207     case KERN_ABORTED:
208         goto restart;
209     default:
210         lose("%s: os_sem_wait(%p): %lu, %s", what, sem, ret, strerror(errno));
211     }
212 }
213
214 void
215 os_sem_post(os_sem_t *sem, char *what)
216 {
217     if (KERN_SUCCESS!=semaphore_signal(*sem))
218         lose("%s: os_sem_post(%p): %s", what, sem, strerror(errno));
219     FSHOW((stderr, "%s: os_sem_post(%p) ok\n", what, sem));
220 }
221
222 void
223 os_sem_destroy(os_sem_t *sem)
224 {
225     if (-1==semaphore_destroy(current_mach_task, *sem))
226         lose("os_sem_destroy(%p): %s", sem, strerror(errno));
227 }
228
229 #endif