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