]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
moved signal handlers into vm object
[factor.git] / vm / os-unix.cpp
1 #include "master.hpp"
2
3 namespace factor
4 {
5
6 THREADHANDLE start_thread(void *(*start_routine)(void *),void *args)
7 {
8         pthread_attr_t attr;
9         pthread_t thread;
10         if (pthread_attr_init (&attr) != 0)
11                 fatal_error("pthread_attr_init() failed",0);
12         if (pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE) != 0)
13                 fatal_error("pthread_attr_setdetachstate() failed",0);
14         if (pthread_create (&thread, &attr, start_routine, args) != 0)
15                 fatal_error("pthread_create() failed",0);
16         pthread_attr_destroy (&attr);
17         return thread;
18 }
19
20
21 pthread_key_t tlsKey = 0;
22
23 void init_platform_globals()
24 {
25         if (pthread_key_create(&tlsKey, NULL) != 0){
26                 fatal_error("pthread_key_create() failed",0);
27         }
28
29 }
30
31 void register_vm_with_thread(factorvm *vm)
32 {
33         pthread_setspecific(tlsKey,vm);
34 }
35
36 factorvm *tls_vm()
37 {
38         return (factorvm*)pthread_getspecific(tlsKey);
39 }
40
41 static void *null_dll;
42
43 s64 current_micros()
44 {
45         struct timeval t;
46         gettimeofday(&t,NULL);
47         return (s64)t.tv_sec * 1000000 + t.tv_usec;
48 }
49
50 void sleep_micros(cell usec)
51 {
52         usleep(usec);
53 }
54
55 void factorvm::init_ffi()
56 {
57         /* NULL_DLL is "libfactor.dylib" for OS X and NULL for generic unix */
58         null_dll = dlopen(NULL_DLL,RTLD_LAZY);
59 }
60
61 void factorvm::ffi_dlopen(dll *dll)
62 {
63         dll->dll = dlopen(alien_offset(dll->path), RTLD_LAZY);
64 }
65
66 void *factorvm::ffi_dlsym(dll *dll, symbol_char *symbol)
67 {
68         void *handle = (dll == NULL ? null_dll : dll->dll);
69         return dlsym(handle,symbol);
70 }
71
72 void factorvm::ffi_dlclose(dll *dll)
73 {
74         if(dlclose(dll->dll))
75                 general_error(ERROR_FFI,F,F,NULL);
76         dll->dll = NULL;
77 }
78
79
80
81
82 inline void factorvm::vmprim_existsp()
83 {
84         struct stat sb;
85         char *path = (char *)(untag_check<byte_array>(dpop()) + 1);
86         box_boolean(stat(path,&sb) >= 0);
87 }
88
89 PRIMITIVE(existsp)
90 {
91         PRIMITIVE_GETVM()->vmprim_existsp();
92 }
93
94 segment *factorvm::alloc_segment(cell size)
95 {
96         int pagesize = getpagesize();
97
98         char *array = (char *)mmap(NULL,pagesize + size + pagesize,
99                 PROT_READ | PROT_WRITE | PROT_EXEC,
100                 MAP_ANON | MAP_PRIVATE,-1,0);
101
102         if(array == (char*)-1)
103                 out_of_memory();
104
105         if(mprotect(array,pagesize,PROT_NONE) == -1)
106                 fatal_error("Cannot protect low guard page",(cell)array);
107
108         if(mprotect(array + pagesize + size,pagesize,PROT_NONE) == -1)
109                 fatal_error("Cannot protect high guard page",(cell)array);
110
111         segment *retval = (segment *)safe_malloc(sizeof(segment));
112
113         retval->start = (cell)(array + pagesize);
114         retval->size = size;
115         retval->end = retval->start + size;
116
117         return retval;
118 }
119
120 void dealloc_segment(segment *block)
121 {
122         int pagesize = getpagesize();
123
124         int retval = munmap((void*)(block->start - pagesize),
125                 pagesize + block->size + pagesize);
126         
127         if(retval)
128                 fatal_error("dealloc_segment failed",0);
129
130         free(block);
131 }
132   
133 stack_frame *factorvm::uap_stack_pointer(void *uap)
134 {
135         /* There is a race condition here, but in practice a signal
136         delivered during stack frame setup/teardown or while transitioning
137         from Factor to C is a sign of things seriously gone wrong, not just
138         a divide by zero or stack underflow in the listener */
139         if(in_code_heap_p(UAP_PROGRAM_COUNTER(uap)))
140         {
141                 stack_frame *ptr = (stack_frame *)ucontext_stack_pointer(uap);
142                 if(!ptr)
143                         critical_error("Invalid uap",(cell)uap);
144                 return ptr;
145         }
146         else
147                 return NULL;
148 }
149
150
151
152 void factorvm::memory_signal_handler(int signal, siginfo_t *siginfo, void *uap)
153 {
154         signal_fault_addr = (cell)siginfo->si_addr;
155         signal_callstack_top = uap_stack_pointer(uap);
156         UAP_PROGRAM_COUNTER(uap) = (cell)factor::memory_signal_handler_impl;
157 }
158
159 void memory_signal_handler(int signal, siginfo_t *siginfo, void *uap)
160 {
161         SIGNAL_VM_PTR()->memory_signal_handler(signal,siginfo,uap);
162 }
163
164
165 void factorvm::misc_signal_handler(int signal, siginfo_t *siginfo, void *uap)
166 {
167         signal_number = signal;
168         signal_callstack_top = uap_stack_pointer(uap);
169         UAP_PROGRAM_COUNTER(uap) = (cell)factor::misc_signal_handler_impl;
170 }
171
172 void misc_signal_handler(int signal, siginfo_t *siginfo, void *uap)
173 {
174         SIGNAL_VM_PTR()->misc_signal_handler(signal,siginfo,uap);
175 }
176
177 void fpe_signal_handler(int signal, siginfo_t *siginfo, void *uap)
178 {
179         signal_number = signal;
180         signal_callstack_top = uap_stack_pointer(uap);
181         signal_fpu_status = fpu_status(uap_fpu_status(uap));
182         uap_clear_fpu_status(uap);
183         UAP_PROGRAM_COUNTER(uap) =
184             (siginfo->si_code == FPE_INTDIV || siginfo->si_code == FPE_INTOVF)
185                 ? (cell)misc_signal_handler_impl
186                 : (cell)fp_signal_handler_impl;
187 }
188
189 static void sigaction_safe(int signum, const struct sigaction *act, struct sigaction *oldact)
190 {
191         int ret;
192         do
193         {
194                 ret = sigaction(signum, act, oldact);
195         }
196         while(ret == -1 && errno == EINTR);
197
198         if(ret == -1)
199                 fatal_error("sigaction failed", 0);
200 }
201
202 void unix_init_signals()
203 {
204         struct sigaction memory_sigaction;
205         struct sigaction misc_sigaction;
206         struct sigaction fpe_sigaction;
207         struct sigaction ignore_sigaction;
208
209         memset(&memory_sigaction,0,sizeof(struct sigaction));
210         sigemptyset(&memory_sigaction.sa_mask);
211         memory_sigaction.sa_sigaction = memory_signal_handler;
212         memory_sigaction.sa_flags = SA_SIGINFO;
213
214         sigaction_safe(SIGBUS,&memory_sigaction,NULL);
215         sigaction_safe(SIGSEGV,&memory_sigaction,NULL);
216
217         memset(&fpe_sigaction,0,sizeof(struct sigaction));
218         sigemptyset(&fpe_sigaction.sa_mask);
219         fpe_sigaction.sa_sigaction = fpe_signal_handler;
220         fpe_sigaction.sa_flags = SA_SIGINFO;
221
222         sigaction_safe(SIGFPE,&fpe_sigaction,NULL);
223
224         memset(&misc_sigaction,0,sizeof(struct sigaction));
225         sigemptyset(&misc_sigaction.sa_mask);
226         misc_sigaction.sa_sigaction = misc_signal_handler;
227         misc_sigaction.sa_flags = SA_SIGINFO;
228
229         sigaction_safe(SIGABRT,&misc_sigaction,NULL);
230         sigaction_safe(SIGQUIT,&misc_sigaction,NULL);
231         sigaction_safe(SIGILL,&misc_sigaction,NULL);
232
233         memset(&ignore_sigaction,0,sizeof(struct sigaction));
234         sigemptyset(&ignore_sigaction.sa_mask);
235         ignore_sigaction.sa_handler = SIG_IGN;
236         sigaction_safe(SIGPIPE,&ignore_sigaction,NULL);
237 }
238
239 /* On Unix, shared fds such as stdin cannot be set to non-blocking mode
240 (http://homepages.tesco.net/J.deBoynePollard/FGA/dont-set-shared-file-descriptors-to-non-blocking-mode.html)
241 so we kludge around this by spawning a thread, which waits on a control pipe
242 for a signal, upon receiving this signal it reads one block of data from stdin
243 and writes it to a data pipe. Upon completion, it writes a 4-byte integer to
244 the size pipe, indicating how much data was written to the data pipe.
245
246 The read end of the size pipe can be set to non-blocking. */
247 extern "C" {
248         int stdin_read;
249         int stdin_write;
250
251         int control_read;
252         int control_write;
253
254         int size_read;
255         int size_write;
256 }
257
258 void safe_close(int fd)
259 {
260         if(close(fd) < 0)
261                 fatal_error("error closing fd",errno);
262 }
263
264 bool check_write(int fd, void *data, ssize_t size)
265 {
266         if(write(fd,data,size) == size)
267                 return true;
268         else
269         {
270                 if(errno == EINTR)
271                         return check_write(fd,data,size);
272                 else
273                         return false;
274         }
275 }
276
277 void safe_write(int fd, void *data, ssize_t size)
278 {
279         if(!check_write(fd,data,size))
280                 fatal_error("error writing fd",errno);
281 }
282
283 bool safe_read(int fd, void *data, ssize_t size)
284 {
285         ssize_t bytes = read(fd,data,size);
286         if(bytes < 0)
287         {
288                 if(errno == EINTR)
289                         return safe_read(fd,data,size);
290                 else
291                 {
292                         fatal_error("error reading fd",errno);
293                         return false;
294                 }
295         }
296         else
297                 return (bytes == size);
298 }
299
300 void *stdin_loop(void *arg)
301 {
302         unsigned char buf[4096];
303         bool loop_running = true;
304
305         while(loop_running)
306         {
307                 if(!safe_read(control_read,buf,1))
308                         break;
309
310                 if(buf[0] != 'X')
311                         fatal_error("stdin_loop: bad data on control fd",buf[0]);
312
313                 for(;;)
314                 {
315                         ssize_t bytes = read(0,buf,sizeof(buf));
316                         if(bytes < 0)
317                         {
318                                 if(errno == EINTR)
319                                         continue;
320                                 else
321                                 {
322                                         loop_running = false;
323                                         break;
324                                 }
325                         }
326                         else if(bytes >= 0)
327                         {
328                                 safe_write(size_write,&bytes,sizeof(bytes));
329
330                                 if(!check_write(stdin_write,buf,bytes))
331                                         loop_running = false;
332                                 break;
333                         }
334                 }
335         }
336
337         safe_close(stdin_write);
338         safe_close(control_read);
339
340         return NULL;
341 }
342
343 void open_console()
344 {
345         int filedes[2];
346
347         if(pipe(filedes) < 0)
348                 fatal_error("Error opening control pipe",errno);
349
350         control_read = filedes[0];
351         control_write = filedes[1];
352
353         if(pipe(filedes) < 0)
354                 fatal_error("Error opening size pipe",errno);
355
356         size_read = filedes[0];
357         size_write = filedes[1];
358
359         if(pipe(filedes) < 0)
360                 fatal_error("Error opening stdin pipe",errno);
361
362         stdin_read = filedes[0];
363         stdin_write = filedes[1];
364
365         start_thread(stdin_loop,NULL);
366 }
367
368 VM_C_API void wait_for_stdin()
369 {
370         if(write(control_write,"X",1) != 1)
371         {
372                 if(errno == EINTR)
373                         wait_for_stdin();
374                 else
375                         fatal_error("Error writing control fd",errno);
376         }
377 }
378
379 }