]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
Merge Phil Dawes' VM work
[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 factorvm::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)factor::misc_signal_handler_impl
186                 : (cell)factor::fp_signal_handler_impl;
187 }
188
189 void fpe_signal_handler(int signal, siginfo_t *siginfo, void *uap)
190 {
191         SIGNAL_VM_PTR()->fpe_signal_handler(signal, siginfo, uap);
192 }
193
194 static void sigaction_safe(int signum, const struct sigaction *act, struct sigaction *oldact)
195 {
196         int ret;
197         do
198         {
199                 ret = sigaction(signum, act, oldact);
200         }
201         while(ret == -1 && errno == EINTR);
202
203         if(ret == -1)
204                 fatal_error("sigaction failed", 0);
205 }
206
207 void unix_init_signals()
208 {
209         struct sigaction memory_sigaction;
210         struct sigaction misc_sigaction;
211         struct sigaction fpe_sigaction;
212         struct sigaction ignore_sigaction;
213
214         memset(&memory_sigaction,0,sizeof(struct sigaction));
215         sigemptyset(&memory_sigaction.sa_mask);
216         memory_sigaction.sa_sigaction = memory_signal_handler;
217         memory_sigaction.sa_flags = SA_SIGINFO;
218
219         sigaction_safe(SIGBUS,&memory_sigaction,NULL);
220         sigaction_safe(SIGSEGV,&memory_sigaction,NULL);
221
222         memset(&fpe_sigaction,0,sizeof(struct sigaction));
223         sigemptyset(&fpe_sigaction.sa_mask);
224         fpe_sigaction.sa_sigaction = fpe_signal_handler;
225         fpe_sigaction.sa_flags = SA_SIGINFO;
226
227         sigaction_safe(SIGFPE,&fpe_sigaction,NULL);
228
229         memset(&misc_sigaction,0,sizeof(struct sigaction));
230         sigemptyset(&misc_sigaction.sa_mask);
231         misc_sigaction.sa_sigaction = misc_signal_handler;
232         misc_sigaction.sa_flags = SA_SIGINFO;
233
234         sigaction_safe(SIGABRT,&misc_sigaction,NULL);
235         sigaction_safe(SIGQUIT,&misc_sigaction,NULL);
236         sigaction_safe(SIGILL,&misc_sigaction,NULL);
237
238         memset(&ignore_sigaction,0,sizeof(struct sigaction));
239         sigemptyset(&ignore_sigaction.sa_mask);
240         ignore_sigaction.sa_handler = SIG_IGN;
241         sigaction_safe(SIGPIPE,&ignore_sigaction,NULL);
242 }
243
244 /* On Unix, shared fds such as stdin cannot be set to non-blocking mode
245 (http://homepages.tesco.net/J.deBoynePollard/FGA/dont-set-shared-file-descriptors-to-non-blocking-mode.html)
246 so we kludge around this by spawning a thread, which waits on a control pipe
247 for a signal, upon receiving this signal it reads one block of data from stdin
248 and writes it to a data pipe. Upon completion, it writes a 4-byte integer to
249 the size pipe, indicating how much data was written to the data pipe.
250
251 The read end of the size pipe can be set to non-blocking. */
252 extern "C" {
253         int stdin_read;
254         int stdin_write;
255
256         int control_read;
257         int control_write;
258
259         int size_read;
260         int size_write;
261 }
262
263 void safe_close(int fd)
264 {
265         if(close(fd) < 0)
266                 fatal_error("error closing fd",errno);
267 }
268
269 bool check_write(int fd, void *data, ssize_t size)
270 {
271         if(write(fd,data,size) == size)
272                 return true;
273         else
274         {
275                 if(errno == EINTR)
276                         return check_write(fd,data,size);
277                 else
278                         return false;
279         }
280 }
281
282 void safe_write(int fd, void *data, ssize_t size)
283 {
284         if(!check_write(fd,data,size))
285                 fatal_error("error writing fd",errno);
286 }
287
288 bool safe_read(int fd, void *data, ssize_t size)
289 {
290         ssize_t bytes = read(fd,data,size);
291         if(bytes < 0)
292         {
293                 if(errno == EINTR)
294                         return safe_read(fd,data,size);
295                 else
296                 {
297                         fatal_error("error reading fd",errno);
298                         return false;
299                 }
300         }
301         else
302                 return (bytes == size);
303 }
304
305 void *stdin_loop(void *arg)
306 {
307         unsigned char buf[4096];
308         bool loop_running = true;
309
310         while(loop_running)
311         {
312                 if(!safe_read(control_read,buf,1))
313                         break;
314
315                 if(buf[0] != 'X')
316                         fatal_error("stdin_loop: bad data on control fd",buf[0]);
317
318                 for(;;)
319                 {
320                         ssize_t bytes = read(0,buf,sizeof(buf));
321                         if(bytes < 0)
322                         {
323                                 if(errno == EINTR)
324                                         continue;
325                                 else
326                                 {
327                                         loop_running = false;
328                                         break;
329                                 }
330                         }
331                         else if(bytes >= 0)
332                         {
333                                 safe_write(size_write,&bytes,sizeof(bytes));
334
335                                 if(!check_write(stdin_write,buf,bytes))
336                                         loop_running = false;
337                                 break;
338                         }
339                 }
340         }
341
342         safe_close(stdin_write);
343         safe_close(control_read);
344
345         return NULL;
346 }
347
348 void open_console()
349 {
350         int filedes[2];
351
352         if(pipe(filedes) < 0)
353                 fatal_error("Error opening control pipe",errno);
354
355         control_read = filedes[0];
356         control_write = filedes[1];
357
358         if(pipe(filedes) < 0)
359                 fatal_error("Error opening size pipe",errno);
360
361         size_read = filedes[0];
362         size_write = filedes[1];
363
364         if(pipe(filedes) < 0)
365                 fatal_error("Error opening stdin pipe",errno);
366
367         stdin_read = filedes[0];
368         stdin_write = filedes[1];
369
370         start_thread(stdin_loop,NULL);
371 }
372
373 VM_C_API void wait_for_stdin()
374 {
375         if(write(control_write,"X",1) != 1)
376         {
377                 if(errno == EINTR)
378                         wait_for_stdin();
379                 else
380                         fatal_error("Error writing control fd",errno);
381         }
382 }
383
384 }