]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
vm: set stdin_loop cancellation state
[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         bool stdin_thread_initialized_p = false;
384         THREADHANDLE stdin_thread;
385         pthread_mutex_t stdin_mutex;
386 }
387
388 void safe_close(int fd)
389 {
390         if(close(fd) < 0)
391                 fatal_error("error closing fd",errno);
392 }
393
394 bool check_write(int fd, void *data, ssize_t size)
395 {
396         if(write(fd,data,size) == size)
397                 return true;
398         else
399         {
400                 if(errno == EINTR)
401                         return check_write(fd,data,size);
402                 else
403                         return false;
404         }
405 }
406
407 void safe_write(int fd, void *data, ssize_t size)
408 {
409         if(!check_write(fd,data,size))
410                 fatal_error("error writing fd",errno);
411 }
412
413 void safe_write_nonblock(int fd, void *data, ssize_t size)
414 {
415         if(!check_write(fd,data,size) && errno != EAGAIN)
416                 fatal_error("error writing fd",errno);
417 }
418
419 bool safe_read(int fd, void *data, ssize_t size)
420 {
421         ssize_t bytes = read(fd,data,size);
422         if(bytes < 0)
423         {
424                 if(errno == EINTR)
425                         return safe_read(fd,data,size);
426                 else
427                 {
428                         fatal_error("error reading fd",errno);
429                         return false;
430                 }
431         }
432         else
433                 return (bytes == size);
434 }
435
436 void *stdin_loop(void *arg)
437 {
438         unsigned char buf[4096];
439         bool loop_running = true;
440
441         sigset_t mask;
442         sigfillset(&mask);
443         sigdelset(&mask, SIGUSR2);
444         sigdelset(&mask, SIGTTIN);
445         sigdelset(&mask, SIGTERM);
446         sigdelset(&mask, SIGQUIT);
447         pthread_sigmask(SIG_SETMASK, &mask, NULL);
448
449         int dontcare;
450         pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &dontcare);
451         pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &dontcare);
452
453         while(loop_running)
454         {
455                 if(!safe_read(control_read,buf,1))
456                         break;
457
458                 if(buf[0] != 'X')
459                         fatal_error("stdin_loop: bad data on control fd",buf[0]);
460
461                 for(;;)
462                 {
463                         // If we fep, the parent thread will grab stdin_mutex and send us
464                         // SIGUSR2 to interrupt the read() call.
465                         pthread_mutex_lock(&stdin_mutex);
466                         pthread_mutex_unlock(&stdin_mutex);
467                         ssize_t bytes = read(0,buf,sizeof(buf));
468                         if(bytes < 0)
469                         {
470                                 if(errno == EINTR)
471                                         continue;
472                                 else
473                                 {
474                                         loop_running = false;
475                                         break;
476                                 }
477                         }
478                         else if(bytes >= 0)
479                         {
480                                 safe_write(size_write,&bytes,sizeof(bytes));
481
482                                 if(!check_write(stdin_write,buf,bytes))
483                                         loop_running = false;
484                                 break;
485                         }
486                 }
487         }
488
489         safe_close(stdin_write);
490         safe_close(control_read);
491
492         return NULL;
493 }
494
495 void factor_vm::open_console()
496 {
497         assert(!stdin_thread_initialized_p);
498         safe_pipe(&control_read,&control_write);
499         safe_pipe(&size_read,&size_write);
500         safe_pipe(&stdin_read,&stdin_write);
501         stdin_thread = start_thread(stdin_loop,NULL);
502         stdin_thread_initialized_p = true;
503         pthread_mutex_init(&stdin_mutex, NULL);
504 }
505
506 // This method is used to kill the stdin_loop before exiting from factor.
507 // A Nvidia driver bug on Linux is the reason this has to be done, see:
508 // http://www.nvnews.net/vbulletin/showthread.php?t=164619
509 void factor_vm::close_console()
510 {
511         if (stdin_thread_initialized_p)
512                 pthread_cancel(stdin_thread);
513 }
514
515 void factor_vm::lock_console()
516 {
517         assert(stdin_thread_initialized_p);
518         // Lock the stdin_mutex and send the stdin_loop thread a signal to interrupt
519         // any read() it has in progress. When the stdin loop iterates again, it will
520         // try to lock the same mutex and wait until unlock_console() is called.
521         pthread_mutex_lock(&stdin_mutex);
522         pthread_kill(stdin_thread, SIGUSR2);
523 }
524
525 void factor_vm::unlock_console()
526 {
527         assert(stdin_thread_initialized_p);
528         pthread_mutex_unlock(&stdin_mutex);
529 }
530
531 void factor_vm::abort()
532 {
533         sig_t ret;
534         do
535         {
536                 ret = signal(SIGABRT, SIG_DFL);
537         }
538         while(ret == SIG_ERR && errno == EINTR);
539         
540         close_console();
541         ::abort();
542 }
543
544 }