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