]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
Merge branch 'master' of git://factorcode.org/git/factor
[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         factor_vm *vm = (factor_vm*)pthread_getspecific(tlsKey);
37         assert(vm != NULL);
38         return vm;
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 factor_vm::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 factor_vm::ffi_dlopen(dll *dll)
62 {
63         dll->dll = dlopen(alien_offset(dll->path), RTLD_LAZY);
64 }
65
66 void *factor_vm::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 factor_vm::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 void factor_vm::primitive_existsp()
80 {
81         struct stat sb;
82         char *path = (char *)(untag_check<byte_array>(dpop()) + 1);
83         box_boolean(stat(path,&sb) >= 0);
84 }
85
86 segment::segment(cell size_, bool executable_p)
87 {
88         size = size_;
89
90         int pagesize = getpagesize();
91
92         int prot;
93         if(executable_p)
94                 prot = (PROT_READ | PROT_WRITE | PROT_EXEC);
95         else
96                 prot = (PROT_READ | PROT_WRITE);
97
98         char *array = (char *)mmap(NULL,pagesize + size + pagesize,prot,MAP_ANON | MAP_PRIVATE,-1,0);
99         if(array == (char*)-1) out_of_memory();
100
101         if(mprotect(array,pagesize,PROT_NONE) == -1)
102                 fatal_error("Cannot protect low guard page",(cell)array);
103
104         if(mprotect(array + pagesize + size,pagesize,PROT_NONE) == -1)
105                 fatal_error("Cannot protect high guard page",(cell)array);
106
107         start = (cell)(array + pagesize);
108         end = start + size;
109 }
110
111 segment::~segment()
112 {
113         int pagesize = getpagesize();
114         int retval = munmap((void*)(start - pagesize),pagesize + size + pagesize);
115         if(retval)
116                 fatal_error("Segment deallocation failed",0);
117 }
118   
119 stack_frame *factor_vm::uap_stack_pointer(void *uap)
120 {
121         /* There is a race condition here, but in practice a signal
122         delivered during stack frame setup/teardown or while transitioning
123         from Factor to C is a sign of things seriously gone wrong, not just
124         a divide by zero or stack underflow in the listener */
125         if(in_code_heap_p(UAP_PROGRAM_COUNTER(uap)))
126         {
127                 stack_frame *ptr = (stack_frame *)ucontext_stack_pointer(uap);
128                 if(!ptr)
129                         critical_error("Invalid uap",(cell)uap);
130                 return ptr;
131         }
132         else
133                 return NULL;
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         tls_vm()->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         tls_vm()->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         tls_vm()->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(SIGQUIT,&misc_sigaction,NULL);
218         sigaction_safe(SIGILL,&misc_sigaction,NULL);
219
220         memset(&ignore_sigaction,0,sizeof(struct sigaction));
221         sigemptyset(&ignore_sigaction.sa_mask);
222         ignore_sigaction.sa_handler = SIG_IGN;
223         sigaction_safe(SIGPIPE,&ignore_sigaction,NULL);
224 }
225
226 /* On Unix, shared fds such as stdin cannot be set to non-blocking mode
227 (http://homepages.tesco.net/J.deBoynePollard/FGA/dont-set-shared-file-descriptors-to-non-blocking-mode.html)
228 so we kludge around this by spawning a thread, which waits on a control pipe
229 for a signal, upon receiving this signal it reads one block of data from stdin
230 and writes it to a data pipe. Upon completion, it writes a 4-byte integer to
231 the size pipe, indicating how much data was written to the data pipe.
232
233 The read end of the size pipe can be set to non-blocking. */
234 extern "C" {
235         int stdin_read;
236         int stdin_write;
237
238         int control_read;
239         int control_write;
240
241         int size_read;
242         int size_write;
243 }
244
245 void safe_close(int fd)
246 {
247         if(close(fd) < 0)
248                 fatal_error("error closing fd",errno);
249 }
250
251 bool check_write(int fd, void *data, ssize_t size)
252 {
253         if(write(fd,data,size) == size)
254                 return true;
255         else
256         {
257                 if(errno == EINTR)
258                         return check_write(fd,data,size);
259                 else
260                         return false;
261         }
262 }
263
264 void safe_write(int fd, void *data, ssize_t size)
265 {
266         if(!check_write(fd,data,size))
267                 fatal_error("error writing fd",errno);
268 }
269
270 bool safe_read(int fd, void *data, ssize_t size)
271 {
272         ssize_t bytes = read(fd,data,size);
273         if(bytes < 0)
274         {
275                 if(errno == EINTR)
276                         return safe_read(fd,data,size);
277                 else
278                 {
279                         fatal_error("error reading fd",errno);
280                         return false;
281                 }
282         }
283         else
284                 return (bytes == size);
285 }
286
287 void *stdin_loop(void *arg)
288 {
289         unsigned char buf[4096];
290         bool loop_running = true;
291
292         while(loop_running)
293         {
294                 if(!safe_read(control_read,buf,1))
295                         break;
296
297                 if(buf[0] != 'X')
298                         fatal_error("stdin_loop: bad data on control fd",buf[0]);
299
300                 for(;;)
301                 {
302                         ssize_t bytes = read(0,buf,sizeof(buf));
303                         if(bytes < 0)
304                         {
305                                 if(errno == EINTR)
306                                         continue;
307                                 else
308                                 {
309                                         loop_running = false;
310                                         break;
311                                 }
312                         }
313                         else if(bytes >= 0)
314                         {
315                                 safe_write(size_write,&bytes,sizeof(bytes));
316
317                                 if(!check_write(stdin_write,buf,bytes))
318                                         loop_running = false;
319                                 break;
320                         }
321                 }
322         }
323
324         safe_close(stdin_write);
325         safe_close(control_read);
326
327         return NULL;
328 }
329
330 void open_console()
331 {
332         int filedes[2];
333
334         if(pipe(filedes) < 0)
335                 fatal_error("Error opening control pipe",errno);
336
337         control_read = filedes[0];
338         control_write = filedes[1];
339
340         if(pipe(filedes) < 0)
341                 fatal_error("Error opening size pipe",errno);
342
343         size_read = filedes[0];
344         size_write = filedes[1];
345
346         if(pipe(filedes) < 0)
347                 fatal_error("Error opening stdin pipe",errno);
348
349         stdin_read = filedes[0];
350         stdin_write = filedes[1];
351
352         start_thread(stdin_loop,NULL);
353 }
354
355 VM_C_API void wait_for_stdin()
356 {
357         if(write(control_write,"X",1) != 1)
358         {
359                 if(errno == EINTR)
360                         wait_for_stdin();
361                 else
362                         fatal_error("Error writing control fd",errno);
363         }
364 }
365
366 }