]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
65e9f0300cd90fa5618ce0a9b6480b2174a28233
[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 static void *null_dll;
21
22 void sleep_nanos(u64 nsec)
23 {
24         timespec ts;
25         timespec ts_rem;
26         int ret;
27         ts.tv_sec = nsec / 1000000000;
28         ts.tv_nsec = nsec % 1000000000;
29         ret = nanosleep(&ts,&ts_rem);
30         while(ret == -1 && errno == EINTR)
31         {
32                 memcpy(&ts, &ts_rem, sizeof(ts));
33                 ret = nanosleep(&ts, &ts_rem);
34         }
35
36         if(ret == -1)
37                 fatal_error("nanosleep failed", 0);
38 }
39
40 void factor_vm::init_ffi()
41 {
42         null_dll = dlopen(NULL,RTLD_LAZY);
43 }
44
45 void factor_vm::ffi_dlopen(dll *dll)
46 {
47         dll->handle = dlopen(alien_offset(dll->path), RTLD_LAZY);
48 }
49
50 void *factor_vm::ffi_dlsym_raw(dll *dll, symbol_char *symbol)
51 {
52         return dlsym(dll ? dll->handle : null_dll, symbol);
53 }
54
55 void *factor_vm::ffi_dlsym(dll *dll, symbol_char *symbol)
56 {
57         return FUNCTION_CODE_POINTER(ffi_dlsym_raw(dll, symbol));
58 }
59
60 #ifdef FACTOR_PPC
61 void *factor_vm::ffi_dlsym_toc(dll *dll, symbol_char *symbol)
62 {
63         return FUNCTION_TOC_POINTER(ffi_dlsym_raw(dll, symbol));
64 }
65 #endif
66
67 void factor_vm::ffi_dlclose(dll *dll)
68 {
69         if(dlclose(dll->handle))
70                 general_error(ERROR_FFI,false_object,false_object);
71         dll->handle = NULL;
72 }
73
74 void factor_vm::primitive_existsp()
75 {
76         struct stat sb;
77         char *path = (char *)(untag_check<byte_array>(ctx->pop()) + 1);
78         ctx->push(tag_boolean(stat(path,&sb) >= 0));
79 }
80
81 void factor_vm::move_file(const vm_char *path1, const vm_char *path2)
82 {
83         int ret = 0;
84         do
85         {
86                 ret = rename((path1),(path2));
87         }
88         while(ret < 0 && errno == EINTR);
89
90         if(ret < 0)
91                 general_error(ERROR_IO,tag_fixnum(errno),false_object);
92 }
93
94 segment::segment(cell size_, bool executable_p)
95 {
96         size = size_;
97
98         int pagesize = getpagesize();
99
100         int prot;
101         if(executable_p)
102                 prot = (PROT_READ | PROT_WRITE | PROT_EXEC);
103         else
104                 prot = (PROT_READ | PROT_WRITE);
105
106         char *array = (char *)mmap(NULL,pagesize + size + pagesize,prot,MAP_ANON | MAP_PRIVATE,-1,0);
107         if(array == (char*)-1) out_of_memory();
108
109         if(mprotect(array,pagesize,PROT_NONE) == -1)
110                 fatal_error("Cannot protect low guard page",(cell)array);
111
112         if(mprotect(array + pagesize + size,pagesize,PROT_NONE) == -1)
113                 fatal_error("Cannot protect high guard page",(cell)array);
114
115         start = (cell)(array + pagesize);
116         end = start + size;
117 }
118
119 segment::~segment()
120 {
121         int pagesize = getpagesize();
122         int retval = munmap((void*)(start - pagesize),pagesize + size + pagesize);
123         if(retval)
124                 fatal_error("Segment deallocation failed",0);
125 }
126
127 void code_heap::guard_safepoint()
128 {
129         if(mprotect(safepoint_page,getpagesize(),PROT_NONE) == -1)
130                 fatal_error("Cannot protect safepoint guard page",(cell)safepoint_page);
131 }
132
133 void code_heap::unguard_safepoint()
134 {
135         if(mprotect(safepoint_page,getpagesize(),PROT_WRITE) == -1)
136                 fatal_error("Cannot unprotect safepoint guard page",(cell)safepoint_page);
137 }
138
139 void factor_vm::dispatch_signal(void *uap, void (handler)())
140 {
141         dispatch_signal_handler(
142                 (cell*)&UAP_STACK_POINTER(uap),
143                 (cell*)&UAP_PROGRAM_COUNTER(uap),
144                 (cell)FUNCTION_CODE_POINTER(handler)
145         );
146         UAP_SET_TOC_POINTER(uap, (cell)FUNCTION_TOC_POINTER(handler));
147 }
148
149 void memory_signal_handler(int signal, siginfo_t *siginfo, void *uap)
150 {
151         factor_vm *vm = current_vm();
152         vm->signal_fault_addr = (cell)siginfo->si_addr;
153         vm->dispatch_signal(uap,factor::memory_signal_handler_impl);
154 }
155
156 void synchronous_signal_handler(int signal, siginfo_t *siginfo, void *uap)
157 {
158         factor_vm *vm = current_vm_p();
159         if (vm)
160         {
161                 vm->signal_number = signal;
162                 vm->dispatch_signal(uap,factor::synchronous_signal_handler_impl);
163         } else
164                 fatal_error("Foreign thread received signal ", signal);
165 }
166
167 void enqueue_signal_handler(int signal, siginfo_t *siginfo, void *uap)
168 {
169         factor_vm *vm = current_vm_p();
170         if (vm)
171                 vm->enqueue_safepoint_signal(signal);
172         else
173                 fatal_error("Foreign thread received signal ", signal);
174 }
175
176 void fep_signal_handler(int signal, siginfo_t *siginfo, void *uap)
177 {
178         factor_vm *vm = current_vm_p();
179         if (vm)
180                 vm->enqueue_safepoint_fep();
181         else
182                 fatal_error("Foreign thread received signal ", signal);
183 }
184
185 void sample_signal_handler(int signal, siginfo_t *siginfo, void *uap)
186 {
187         factor_vm *vm = current_vm_p();
188         if (vm)
189                 vm->enqueue_safepoint_sample();
190         else
191                 fatal_error("Foreign thread received signal ", signal);
192 }
193
194 void ignore_signal_handler(int signal, siginfo_t *siginfo, void *uap)
195 {
196 }
197
198 void fpe_signal_handler(int signal, siginfo_t *siginfo, void *uap)
199 {
200         factor_vm *vm = current_vm();
201         vm->signal_number = signal;
202         vm->signal_fpu_status = fpu_status(uap_fpu_status(uap));
203         uap_clear_fpu_status(uap);
204
205         vm->dispatch_signal(uap,
206                 (siginfo->si_code == FPE_INTDIV || siginfo->si_code == FPE_INTOVF)
207                 ? factor::synchronous_signal_handler_impl
208                 : factor::fp_signal_handler_impl);
209 }
210
211 static void sigaction_safe(int signum, const struct sigaction *act, struct sigaction *oldact)
212 {
213         int ret;
214         do
215         {
216                 ret = sigaction(signum, act, oldact);
217         }
218         while(ret == -1 && errno == EINTR);
219
220         if(ret == -1)
221                 fatal_error("sigaction failed", 0);
222 }
223
224 static void init_sigaction_with_handler(struct sigaction *act,
225         void (*handler)(int, siginfo_t*, void*))
226 {
227         memset(act, 0, sizeof(struct sigaction));
228         sigemptyset(&act->sa_mask);
229         act->sa_sigaction = handler;
230         act->sa_flags = SA_SIGINFO | SA_ONSTACK;
231 }
232
233 void factor_vm::unix_init_signals()
234 {
235         /* OpenBSD doesn't support sigaltstack() if we link against
236         libpthread. See http://redmine.ruby-lang.org/issues/show/1239 */
237
238 #ifndef __OpenBSD__
239         signal_callstack_seg = new segment(callstack_size,false);
240
241         stack_t signal_callstack;
242         signal_callstack.ss_sp = (char *)signal_callstack_seg->start;
243         signal_callstack.ss_size = signal_callstack_seg->size;
244         signal_callstack.ss_flags = 0;
245
246         if(sigaltstack(&signal_callstack,(stack_t *)NULL) < 0)
247                 fatal_error("sigaltstack() failed",0);
248 #endif
249
250         struct sigaction memory_sigaction;
251         struct sigaction synchronous_sigaction;
252         struct sigaction enqueue_sigaction;
253         struct sigaction fep_sigaction;
254         struct sigaction sample_sigaction;
255         struct sigaction fpe_sigaction;
256         struct sigaction ignore_sigaction;
257
258         init_sigaction_with_handler(&memory_sigaction, memory_signal_handler);
259         sigaction_safe(SIGBUS,&memory_sigaction,NULL);
260         sigaction_safe(SIGSEGV,&memory_sigaction,NULL);
261         sigaction_safe(SIGTRAP,&memory_sigaction,NULL);
262
263         init_sigaction_with_handler(&fpe_sigaction, fpe_signal_handler);
264         sigaction_safe(SIGFPE,&fpe_sigaction,NULL);
265
266         init_sigaction_with_handler(&synchronous_sigaction, synchronous_signal_handler);
267         sigaction_safe(SIGILL,&synchronous_sigaction,NULL);
268         sigaction_safe(SIGABRT,&synchronous_sigaction,NULL);
269
270         init_sigaction_with_handler(&enqueue_sigaction, enqueue_signal_handler);
271         sigaction_safe(SIGUSR1,&enqueue_sigaction,NULL);
272         sigaction_safe(SIGUSR2,&enqueue_sigaction,NULL);
273         sigaction_safe(SIGWINCH,&enqueue_sigaction,NULL);
274 #ifdef SIGINFO
275         sigaction_safe(SIGINFO,&enqueue_sigaction,NULL);
276 #endif
277
278         init_sigaction_with_handler(&fep_sigaction, fep_signal_handler);
279         sigaction_safe(SIGQUIT,&fep_sigaction,NULL);
280         sigaction_safe(SIGINT,&fep_sigaction,NULL);
281
282         init_sigaction_with_handler(&sample_sigaction, sample_signal_handler);
283         sigaction_safe(SIGALRM,&sample_sigaction,NULL);
284
285         /* We don't use SA_IGN here because then the ignore action is inherited
286         by subprocesses, which we don't want. There is a unit test in
287         io.launcher.unix for this. */
288         init_sigaction_with_handler(&ignore_sigaction, ignore_signal_handler);
289         sigaction_safe(SIGPIPE,&ignore_sigaction,NULL);
290 }
291
292 /* On Unix, shared fds such as stdin cannot be set to non-blocking mode
293 (http://homepages.tesco.net/J.deBoynePollard/FGA/dont-set-shared-file-descriptors-to-non-blocking-mode.html)
294 so we kludge around this by spawning a thread, which waits on a control pipe
295 for a signal, upon receiving this signal it reads one block of data from stdin
296 and writes it to a data pipe. Upon completion, it writes a 4-byte integer to
297 the size pipe, indicating how much data was written to the data pipe.
298
299 The read end of the size pipe can be set to non-blocking. */
300 extern "C" {
301         int stdin_read;
302         int stdin_write;
303
304         int control_read;
305         int control_write;
306
307         int size_read;
308         int size_write;
309 }
310
311 void safe_close(int fd)
312 {
313         if(close(fd) < 0)
314                 fatal_error("error closing fd",errno);
315 }
316
317 bool check_write(int fd, void *data, ssize_t size)
318 {
319         if(write(fd,data,size) == size)
320                 return true;
321         else
322         {
323                 if(errno == EINTR)
324                         return check_write(fd,data,size);
325                 else
326                         return false;
327         }
328 }
329
330 void safe_write(int fd, void *data, ssize_t size)
331 {
332         if(!check_write(fd,data,size))
333                 fatal_error("error writing fd",errno);
334 }
335
336 bool safe_read(int fd, void *data, ssize_t size)
337 {
338         ssize_t bytes = read(fd,data,size);
339         if(bytes < 0)
340         {
341                 if(errno == EINTR)
342                         return safe_read(fd,data,size);
343                 else
344                 {
345                         fatal_error("error reading fd",errno);
346                         return false;
347                 }
348         }
349         else
350                 return (bytes == size);
351 }
352
353 void *stdin_loop(void *arg)
354 {
355         unsigned char buf[4096];
356         bool loop_running = true;
357
358         sigset_t mask;
359         sigfillset(&mask);
360         pthread_sigmask(SIG_BLOCK, &mask, NULL);
361
362         while(loop_running)
363         {
364                 if(!safe_read(control_read,buf,1))
365                         break;
366
367                 if(buf[0] != 'X')
368                         fatal_error("stdin_loop: bad data on control fd",buf[0]);
369
370                 for(;;)
371                 {
372                         ssize_t bytes = read(0,buf,sizeof(buf));
373                         if(bytes < 0)
374                         {
375                                 if(errno == EINTR)
376                                         continue;
377                                 else
378                                 {
379                                         loop_running = false;
380                                         break;
381                                 }
382                         }
383                         else if(bytes >= 0)
384                         {
385                                 safe_write(size_write,&bytes,sizeof(bytes));
386
387                                 if(!check_write(stdin_write,buf,bytes))
388                                         loop_running = false;
389                                 break;
390                         }
391                 }
392         }
393
394         safe_close(stdin_write);
395         safe_close(control_read);
396
397         return NULL;
398 }
399
400 void safe_pipe(int *in, int *out)
401 {
402         int filedes[2];
403
404         if(pipe(filedes) < 0)
405                 fatal_error("Error opening pipe",errno);
406
407         *in = filedes[0];
408         *out = filedes[1];
409
410         if(fcntl(*in,F_SETFD,FD_CLOEXEC) < 0)
411                 fatal_error("Error with fcntl",errno);
412
413         if(fcntl(*out,F_SETFD,FD_CLOEXEC) < 0)
414                 fatal_error("Error with fcntl",errno);
415 }
416
417 void open_console()
418 {
419         safe_pipe(&control_read,&control_write);
420         safe_pipe(&size_read,&size_write);
421         safe_pipe(&stdin_read,&stdin_write);
422         start_thread(stdin_loop,NULL);
423 }
424
425 }