]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
vm: close the console when exiting
[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 factor_vm::start_sampling_profiler_timer()
150 {
151         struct itimerval timer;
152         memset((void*)&timer, 0, sizeof(struct itimerval));
153         timer.it_value.tv_usec = 1000000/samples_per_second;
154         timer.it_interval.tv_usec = 1000000/samples_per_second;
155         setitimer(ITIMER_REAL, &timer, NULL);
156 }
157
158 void factor_vm::end_sampling_profiler_timer()
159 {
160         struct itimerval timer;
161         memset((void*)&timer, 0, sizeof(struct itimerval));
162         setitimer(ITIMER_REAL, &timer, NULL);
163 }
164
165 void memory_signal_handler(int signal, siginfo_t *siginfo, void *uap)
166 {
167         factor_vm *vm = current_vm();
168         vm->verify_memory_protection_error((cell)siginfo->si_addr);
169         vm->signal_fault_addr = (cell)siginfo->si_addr;
170         vm->dispatch_signal(uap,factor::memory_signal_handler_impl);
171 }
172
173 void synchronous_signal_handler(int signal, siginfo_t *siginfo, void *uap)
174 {
175         if (factor_vm::fatal_erroring_p)
176                 return;
177
178         factor_vm *vm = current_vm_p();
179         if (vm)
180         {
181                 vm->signal_number = signal;
182                 vm->dispatch_signal(uap,factor::synchronous_signal_handler_impl);
183         }
184         else
185                 fatal_error("Foreign thread received signal", signal);
186 }
187
188 void safe_write_nonblock(int fd, void *data, ssize_t size);
189
190 static void enqueue_signal(factor_vm *vm, int signal)
191 {
192         if (vm->signal_pipe_output != 0)
193                 safe_write_nonblock(vm->signal_pipe_output, &signal, sizeof(int));
194 }
195
196 void enqueue_signal_handler(int signal, siginfo_t *siginfo, void *uap)
197 {
198         if (factor_vm::fatal_erroring_p)
199                 return;
200
201         factor_vm *vm = current_vm_p();
202         if (vm)
203                 enqueue_signal(vm, signal);
204         else
205                 fatal_error("Foreign thread received signal", signal);
206 }
207
208 void fep_signal_handler(int signal, siginfo_t *siginfo, void *uap)
209 {
210         if (factor_vm::fatal_erroring_p)
211                 return;
212
213         factor_vm *vm = current_vm_p();
214         if (vm)
215         {
216                 vm->safepoint.enqueue_fep(vm);
217                 enqueue_signal(vm, signal);
218         }
219         else
220                 fatal_error("Foreign thread received signal", signal);
221 }
222
223 void sample_signal_handler(int signal, siginfo_t *siginfo, void *uap)
224 {
225         factor_vm *vm = current_vm_p();
226         bool foreign_thread = false;
227         if (vm == NULL)
228         {
229                 foreign_thread = true;
230                 vm = thread_vms.begin()->second;
231         }
232         if (atomic::load(&vm->sampling_profiler_p))
233                 vm->safepoint.enqueue_samples(vm, 1, (cell)UAP_PROGRAM_COUNTER(uap), foreign_thread);
234         else if (!foreign_thread)
235                 enqueue_signal(vm, signal);
236 }
237
238 void ignore_signal_handler(int signal, siginfo_t *siginfo, void *uap)
239 {
240 }
241
242 void fpe_signal_handler(int signal, siginfo_t *siginfo, void *uap)
243 {
244         factor_vm *vm = current_vm();
245         vm->signal_number = signal;
246         vm->signal_fpu_status = fpu_status(uap_fpu_status(uap));
247         uap_clear_fpu_status(uap);
248
249         vm->dispatch_signal(uap,
250                 (siginfo->si_code == FPE_INTDIV || siginfo->si_code == FPE_INTOVF)
251                 ? factor::synchronous_signal_handler_impl
252                 : factor::fp_signal_handler_impl);
253 }
254
255 static void sigaction_safe(int signum, const struct sigaction *act, struct sigaction *oldact)
256 {
257         int ret;
258         do
259         {
260                 ret = sigaction(signum, act, oldact);
261         }
262         while(ret == -1 && errno == EINTR);
263
264         if(ret == -1)
265                 fatal_error("sigaction failed", errno);
266 }
267
268 static void init_sigaction_with_handler(struct sigaction *act,
269         void (*handler)(int, siginfo_t*, void*))
270 {
271         memset(act, 0, sizeof(struct sigaction));
272         sigemptyset(&act->sa_mask);
273         act->sa_sigaction = handler;
274         act->sa_flags = SA_SIGINFO | SA_ONSTACK;
275 }
276
277 static void safe_pipe(int *in, int *out)
278 {
279         int filedes[2];
280
281         if(pipe(filedes) < 0)
282                 fatal_error("Error opening pipe",errno);
283
284         *in = filedes[0];
285         *out = filedes[1];
286
287         if(fcntl(*in,F_SETFD,FD_CLOEXEC) < 0)
288                 fatal_error("Error with fcntl",errno);
289
290         if(fcntl(*out,F_SETFD,FD_CLOEXEC) < 0)
291                 fatal_error("Error with fcntl",errno);
292 }
293
294 static void init_signal_pipe(factor_vm *vm)
295 {
296         safe_pipe(&vm->signal_pipe_input, &vm->signal_pipe_output);
297
298         if(fcntl(vm->signal_pipe_output,F_SETFL,O_NONBLOCK) < 0)
299                 fatal_error("Error with fcntl",errno);
300
301         vm->special_objects[OBJ_SIGNAL_PIPE] = tag_fixnum(vm->signal_pipe_input);
302 }
303
304 void factor_vm::unix_init_signals()
305 {
306         init_signal_pipe(this);
307
308         signal_callstack_seg = new segment(callstack_size,false);
309
310         stack_t signal_callstack;
311         signal_callstack.ss_sp = (char *)signal_callstack_seg->start;
312         signal_callstack.ss_size = signal_callstack_seg->size;
313         signal_callstack.ss_flags = 0;
314
315         if(sigaltstack(&signal_callstack,(stack_t *)NULL) < 0)
316                 fatal_error("sigaltstack() failed",0);
317
318         struct sigaction memory_sigaction;
319         struct sigaction synchronous_sigaction;
320         struct sigaction enqueue_sigaction;
321         struct sigaction fep_sigaction;
322         struct sigaction sample_sigaction;
323         struct sigaction fpe_sigaction;
324         struct sigaction ignore_sigaction;
325
326         init_sigaction_with_handler(&memory_sigaction, memory_signal_handler);
327         sigaction_safe(SIGBUS,&memory_sigaction,NULL);
328         sigaction_safe(SIGSEGV,&memory_sigaction,NULL);
329         sigaction_safe(SIGTRAP,&memory_sigaction,NULL);
330
331         init_sigaction_with_handler(&fpe_sigaction, fpe_signal_handler);
332         sigaction_safe(SIGFPE,&fpe_sigaction,NULL);
333
334         init_sigaction_with_handler(&synchronous_sigaction, synchronous_signal_handler);
335         sigaction_safe(SIGILL,&synchronous_sigaction,NULL);
336         sigaction_safe(SIGABRT,&synchronous_sigaction,NULL);
337
338         init_sigaction_with_handler(&enqueue_sigaction, enqueue_signal_handler);
339         sigaction_safe(SIGWINCH,&enqueue_sigaction,NULL);
340         sigaction_safe(SIGUSR1,&enqueue_sigaction,NULL);
341         sigaction_safe(SIGCONT,&enqueue_sigaction,NULL);
342         sigaction_safe(SIGURG,&enqueue_sigaction,NULL);
343         sigaction_safe(SIGIO,&enqueue_sigaction,NULL);
344         sigaction_safe(SIGPROF,&enqueue_sigaction,NULL);
345         sigaction_safe(SIGVTALRM,&enqueue_sigaction,NULL);
346 #ifdef SIGINFO
347         sigaction_safe(SIGINFO,&enqueue_sigaction,NULL);
348 #endif
349
350         init_sigaction_with_handler(&fep_sigaction, fep_signal_handler);
351         sigaction_safe(SIGINT,&fep_sigaction,NULL);
352
353         init_sigaction_with_handler(&sample_sigaction, sample_signal_handler);
354         sigaction_safe(SIGALRM,&sample_sigaction,NULL);
355
356         /* We don't use SA_IGN here because then the ignore action is inherited
357         by subprocesses, which we don't want. There is a unit test in
358         io.launcher.unix for this. */
359         init_sigaction_with_handler(&ignore_sigaction, ignore_signal_handler);
360         sigaction_safe(SIGPIPE,&ignore_sigaction,NULL);
361         /* We send SIGUSR2 to the stdin_loop thread to interrupt it on FEP */
362         sigaction_safe(SIGUSR2,&ignore_sigaction,NULL);
363 }
364
365 /* On Unix, shared fds such as stdin cannot be set to non-blocking mode
366 (http://homepages.tesco.net/J.deBoynePollard/FGA/dont-set-shared-file-descriptors-to-non-blocking-mode.html)
367 so we kludge around this by spawning a thread, which waits on a control pipe
368 for a signal, upon receiving this signal it reads one block of data from stdin
369 and writes it to a data pipe. Upon completion, it writes a 4-byte integer to
370 the size pipe, indicating how much data was written to the data pipe.
371
372 The read end of the size pipe can be set to non-blocking. */
373 extern "C" {
374         int stdin_read;
375         int stdin_write;
376
377         int control_read;
378         int control_write;
379
380         int size_read;
381         int size_write;
382
383         THREADHANDLE stdin_thread;
384         pthread_mutex_t stdin_mutex;
385 }
386
387 void safe_close(int fd)
388 {
389         if(close(fd) < 0)
390                 fatal_error("error closing fd",errno);
391 }
392
393 bool check_write(int fd, void *data, ssize_t size)
394 {
395         if(write(fd,data,size) == size)
396                 return true;
397         else
398         {
399                 if(errno == EINTR)
400                         return check_write(fd,data,size);
401                 else
402                         return false;
403         }
404 }
405
406 void safe_write(int fd, void *data, ssize_t size)
407 {
408         if(!check_write(fd,data,size))
409                 fatal_error("error writing fd",errno);
410 }
411
412 void safe_write_nonblock(int fd, void *data, ssize_t size)
413 {
414         if(!check_write(fd,data,size) && errno != EAGAIN)
415                 fatal_error("error writing fd",errno);
416 }
417
418 bool safe_read(int fd, void *data, ssize_t size)
419 {
420         ssize_t bytes = read(fd,data,size);
421         if(bytes < 0)
422         {
423                 if(errno == EINTR)
424                         return safe_read(fd,data,size);
425                 else
426                 {
427                         fatal_error("error reading fd",errno);
428                         return false;
429                 }
430         }
431         else
432                 return (bytes == size);
433 }
434
435 void *stdin_loop(void *arg)
436 {
437         unsigned char buf[4096];
438         bool loop_running = true;
439
440         sigset_t mask;
441         sigfillset(&mask);
442         sigdelset(&mask, SIGUSR2);
443         sigdelset(&mask, SIGTTIN);
444         sigdelset(&mask, SIGTERM);
445         sigdelset(&mask, SIGQUIT);
446         pthread_sigmask(SIG_SETMASK, &mask, NULL);
447
448         while(loop_running)
449         {
450                 if(!safe_read(control_read,buf,1))
451                         break;
452
453                 if(buf[0] != 'X')
454                         fatal_error("stdin_loop: bad data on control fd",buf[0]);
455
456                 for(;;)
457                 {
458                         // If we fep, the parent thread will grab stdin_mutex and send us
459                         // SIGUSR2 to interrupt the read() call.
460                         pthread_mutex_lock(&stdin_mutex);
461                         pthread_mutex_unlock(&stdin_mutex);
462                         ssize_t bytes = read(0,buf,sizeof(buf));
463                         if(bytes < 0)
464                         {
465                                 if(errno == EINTR)
466                                         continue;
467                                 else
468                                 {
469                                         loop_running = false;
470                                         break;
471                                 }
472                         }
473                         else if(bytes >= 0)
474                         {
475                                 safe_write(size_write,&bytes,sizeof(bytes));
476
477                                 if(!check_write(stdin_write,buf,bytes))
478                                         loop_running = false;
479                                 break;
480                         }
481                 }
482         }
483
484         safe_close(stdin_write);
485         safe_close(control_read);
486
487         return NULL;
488 }
489
490 void factor_vm::open_console()
491 {
492         safe_pipe(&control_read,&control_write);
493         safe_pipe(&size_read,&size_write);
494         safe_pipe(&stdin_read,&stdin_write);
495         stdin_thread = start_thread(stdin_loop,NULL);
496         pthread_mutex_init(&stdin_mutex, NULL);
497 }
498
499 // This method is used to kill the stdin_loop before exiting from factor.
500 // A Nvidia driver bug on Linux is the reason this has to be done, see:
501 // http://www.nvnews.net/vbulletin/showthread.php?t=164619
502 void factor_vm::close_console()
503 {
504         pthread_mutex_lock(&stdin_mutex);
505         pthread_kill(stdin_thread, SIGTERM);
506 }
507
508 void factor_vm::lock_console()
509 {
510         // Lock the stdin_mutex and send the stdin_loop thread a signal to interrupt
511         // any read() it has in progress. When the stdin loop iterates again, it will
512         // try to lock the same mutex and wait until unlock_console() is called.
513         pthread_mutex_lock(&stdin_mutex);
514         pthread_kill(stdin_thread, SIGUSR2);
515 }
516
517 void factor_vm::unlock_console()
518 {
519         pthread_mutex_unlock(&stdin_mutex);
520 }
521
522 void factor_vm::abort()
523 {
524         sig_t ret;
525         do
526         {
527                 ret = signal(SIGABRT, SIG_DFL);
528         }
529         while(ret == SIG_ERR && errno == EINTR);
530         
531         ::abort();
532 }
533
534 }