]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
VM: replaced calls to out_of_memory() with fatal_error()
[factor.git] / vm / os-unix.cpp
1 #include "master.hpp"
2
3 namespace factor {
4
5 bool set_memory_locked(cell base, cell size, bool locked) {
6   int prot = locked ? PROT_NONE : PROT_READ | PROT_WRITE;
7   int status = mprotect((char*)base, size, prot);
8   return status != -1;
9 }
10
11 THREADHANDLE start_thread(void* (*start_routine)(void*), void* args) {
12   pthread_attr_t attr;
13   pthread_t thread;
14   if (pthread_attr_init(&attr) != 0)
15     fatal_error("pthread_attr_init() failed", 0);
16   if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) != 0)
17     fatal_error("pthread_attr_setdetachstate() failed", 0);
18   if (pthread_create(&thread, &attr, start_routine, args) != 0)
19     fatal_error("pthread_create() failed", 0);
20   pthread_attr_destroy(&attr);
21   return thread;
22 }
23
24 static void* null_dll;
25
26 void sleep_nanos(uint64_t nsec) {
27   timespec ts;
28   timespec ts_rem;
29   int ret;
30   ts.tv_sec = nsec / 1000000000;
31   ts.tv_nsec = nsec % 1000000000;
32   ret = nanosleep(&ts, &ts_rem);
33   while (ret == -1 && errno == EINTR) {
34     memcpy(&ts, &ts_rem, sizeof(ts));
35     ret = nanosleep(&ts, &ts_rem);
36   }
37
38   if (ret == -1)
39     fatal_error("nanosleep failed", 0);
40 }
41
42 void factor_vm::init_ffi() { null_dll = dlopen(NULL, RTLD_LAZY); }
43
44 void factor_vm::ffi_dlopen(dll* dll) {
45   dll->handle = dlopen(alien_offset(dll->path), RTLD_LAZY | RTLD_GLOBAL);
46 }
47
48 cell factor_vm::ffi_dlsym_raw(dll* dll, symbol_char* symbol) {
49   return (cell)dlsym(dll ? dll->handle : null_dll, symbol);
50 }
51
52 cell factor_vm::ffi_dlsym(dll* dll, symbol_char* symbol) {
53   return FUNCTION_CODE_POINTER(ffi_dlsym_raw(dll, symbol));
54 }
55
56 void factor_vm::ffi_dlclose(dll* dll) {
57   if (dlclose(dll->handle))
58     general_error(ERROR_FFI, false_object, false_object);
59   dll->handle = NULL;
60 }
61
62 void factor_vm::primitive_existsp() {
63   struct stat sb;
64   char* path = (char*)(untag_check<byte_array>(ctx->pop()) + 1);
65   ctx->push(tag_boolean(stat(path, &sb) >= 0));
66 }
67
68 bool move_file(const vm_char* path1, const vm_char* path2) {
69   int ret = 0;
70   do {
71     ret = rename((path1), (path2));
72   } while (ret < 0 && errno == EINTR);
73
74   return ret == 0;
75 }
76
77 segment::segment(cell size_, bool executable_p) {
78   size = size_;
79
80   int pagesize = getpagesize();
81
82   int prot;
83   if (executable_p)
84     prot = PROT_READ | PROT_WRITE | PROT_EXEC;
85   else
86     prot = PROT_READ | PROT_WRITE;
87
88   cell alloc_size = 2 * pagesize + size;
89   char* array = (char*)mmap(NULL, alloc_size, prot,
90                             MAP_ANON | MAP_PRIVATE, -1, 0);
91
92   if (array == (char*)-1)
93     fatal_error("Out of memory in mmap", alloc_size);
94
95   start = (cell)(array + pagesize);
96   end = start + size;
97
98   set_border_locked(true);
99 }
100
101 void segment::set_border_locked(bool locked) {
102   int pagesize = getpagesize();
103   cell lo = start - pagesize;
104   if (!set_memory_locked(lo, pagesize, locked)) {
105     fatal_error("Cannot (un)protect low guard page", lo);
106   }
107
108   cell hi = end;
109   if (!set_memory_locked(hi, pagesize, locked)) {
110     fatal_error("Cannot (un)protect high guard page", hi);
111   }
112 }
113
114 segment::~segment() {
115   int pagesize = getpagesize();
116   int retval = munmap((void*)(start - pagesize), pagesize + size + pagesize);
117   if (retval)
118     fatal_error("Segment deallocation failed", 0);
119 }
120
121 void factor_vm::dispatch_signal(void* uap, void(handler)()) {
122   dispatch_signal_handler((cell*)&UAP_STACK_POINTER(uap),
123                           (cell*)&UAP_PROGRAM_COUNTER(uap),
124                           (cell)FUNCTION_CODE_POINTER(handler));
125   UAP_SET_TOC_POINTER(uap, (cell)FUNCTION_TOC_POINTER(handler));
126 }
127
128 void factor_vm::start_sampling_profiler_timer() {
129   struct itimerval timer;
130   memset((void*)&timer, 0, sizeof(struct itimerval));
131   timer.it_value.tv_usec = 1000000 / samples_per_second;
132   timer.it_interval.tv_usec = 1000000 / samples_per_second;
133   setitimer(ITIMER_REAL, &timer, NULL);
134 }
135
136 void factor_vm::end_sampling_profiler_timer() {
137   struct itimerval timer;
138   memset((void*)&timer, 0, sizeof(struct itimerval));
139   setitimer(ITIMER_REAL, &timer, NULL);
140 }
141
142 void memory_signal_handler(int signal, siginfo_t* siginfo, void* uap) {
143
144   cell fault_addr = (cell)siginfo->si_addr;
145   cell fault_pc = (cell)UAP_PROGRAM_COUNTER(uap);
146   factor_vm* vm = current_vm();
147   vm->verify_memory_protection_error(fault_addr);
148   vm->signal_fault_addr = fault_addr;
149   vm->signal_fault_pc = fault_pc;
150   vm->dispatch_signal(uap, factor::memory_signal_handler_impl);
151 }
152
153 void synchronous_signal_handler(int signal, siginfo_t* siginfo, void* uap) {
154   if (factor_vm::fatal_erroring_p)
155     return;
156
157   factor_vm* vm = current_vm_p();
158   if (vm) {
159     vm->signal_number = signal;
160     vm->dispatch_signal(uap, factor::synchronous_signal_handler_impl);
161   } else
162     fatal_error("Foreign thread received signal", signal);
163 }
164
165 void safe_write_nonblock(int fd, void* data, ssize_t size);
166
167 static void enqueue_signal(factor_vm* vm, int signal) {
168   if (vm->signal_pipe_output != 0)
169     safe_write_nonblock(vm->signal_pipe_output, &signal, sizeof(int));
170 }
171
172 void enqueue_signal_handler(int signal, siginfo_t* siginfo, void* uap) {
173   if (factor_vm::fatal_erroring_p)
174     return;
175
176   factor_vm* vm = current_vm_p();
177   if (vm)
178     enqueue_signal(vm, signal);
179 }
180
181 void fep_signal_handler(int signal, siginfo_t* siginfo, void* uap) {
182   if (factor_vm::fatal_erroring_p)
183     return;
184
185   factor_vm* vm = current_vm_p();
186   if (vm) {
187     vm->safepoint.enqueue_fep(vm);
188     enqueue_signal(vm, signal);
189   } else
190     fatal_error("Foreign thread received signal", signal);
191 }
192
193 void sample_signal_handler(int signal, siginfo_t* siginfo, void* uap) {
194   factor_vm* vm = current_vm_p();
195   bool foreign_thread = false;
196   if (vm == NULL) {
197     foreign_thread = true;
198     vm = thread_vms.begin()->second;
199   }
200   if (atomic::load(&vm->sampling_profiler_p))
201     vm->safepoint.enqueue_samples(vm, 1, (cell)UAP_PROGRAM_COUNTER(uap),
202                                   foreign_thread);
203   else if (!foreign_thread)
204     enqueue_signal(vm, signal);
205 }
206
207 void ignore_signal_handler(int signal, siginfo_t* siginfo, void* uap) {}
208
209 void fpe_signal_handler(int signal, siginfo_t* siginfo, void* uap) {
210   factor_vm* vm = current_vm();
211   vm->signal_number = signal;
212   vm->signal_fpu_status = fpu_status(uap_fpu_status(uap));
213   uap_clear_fpu_status(uap);
214
215   vm->dispatch_signal(
216       uap, (siginfo->si_code == FPE_INTDIV || siginfo->si_code == FPE_INTOVF)
217                ? factor::synchronous_signal_handler_impl
218                : factor::fp_signal_handler_impl);
219 }
220
221 static void sigaction_safe(int signum, const struct sigaction* act,
222                            struct sigaction* oldact) {
223   int ret;
224   do {
225     ret = sigaction(signum, act, oldact);
226   } while (ret == -1 && errno == EINTR);
227
228   if (ret == -1)
229     fatal_error("sigaction failed", errno);
230 }
231
232 static void init_sigaction_with_handler(struct sigaction* act,
233                                         void (*handler)(int, siginfo_t*,
234                                                         void*)) {
235   memset(act, 0, sizeof(struct sigaction));
236   sigemptyset(&act->sa_mask);
237   act->sa_sigaction = handler;
238   act->sa_flags = SA_SIGINFO | SA_ONSTACK;
239 }
240
241 static void safe_pipe(int* in, int* out) {
242   int filedes[2];
243
244   if (pipe(filedes) < 0)
245     fatal_error("Error opening pipe", errno);
246
247   *in = filedes[0];
248   *out = filedes[1];
249
250   if (fcntl(*in, F_SETFD, FD_CLOEXEC) < 0)
251     fatal_error("Error with fcntl", errno);
252
253   if (fcntl(*out, F_SETFD, FD_CLOEXEC) < 0)
254     fatal_error("Error with fcntl", errno);
255 }
256
257 static void init_signal_pipe(factor_vm* vm) {
258   safe_pipe(&vm->signal_pipe_input, &vm->signal_pipe_output);
259
260   if (fcntl(vm->signal_pipe_output, F_SETFL, O_NONBLOCK) < 0)
261     fatal_error("Error with fcntl", errno);
262
263   vm->special_objects[OBJ_SIGNAL_PIPE] = tag_fixnum(vm->signal_pipe_input);
264 }
265
266 void factor_vm::unix_init_signals() {
267   init_signal_pipe(this);
268
269   signal_callstack_seg = new segment(callstack_size, false);
270
271   stack_t signal_callstack;
272   signal_callstack.ss_sp = (char*)signal_callstack_seg->start;
273   signal_callstack.ss_size = signal_callstack_seg->size;
274   signal_callstack.ss_flags = 0;
275
276   if (sigaltstack(&signal_callstack, (stack_t*)NULL) < 0)
277     fatal_error("sigaltstack() failed", 0);
278
279   {
280     struct sigaction memory_sigaction;
281     init_sigaction_with_handler(&memory_sigaction, memory_signal_handler);
282     sigaction_safe(SIGBUS, &memory_sigaction, NULL);
283     sigaction_safe(SIGSEGV, &memory_sigaction, NULL);
284     sigaction_safe(SIGTRAP, &memory_sigaction, NULL);
285   }
286
287   {
288     struct sigaction fpe_sigaction;
289     init_sigaction_with_handler(&fpe_sigaction, fpe_signal_handler);
290     sigaction_safe(SIGFPE, &fpe_sigaction, NULL);
291   }
292
293   {
294     struct sigaction synchronous_sigaction;
295     init_sigaction_with_handler(&synchronous_sigaction,
296                                 synchronous_signal_handler);
297     sigaction_safe(SIGILL, &synchronous_sigaction, NULL);
298     sigaction_safe(SIGABRT, &synchronous_sigaction, NULL);
299   }
300
301   {
302     struct sigaction enqueue_sigaction;
303     init_sigaction_with_handler(&enqueue_sigaction, enqueue_signal_handler);
304     sigaction_safe(SIGWINCH, &enqueue_sigaction, NULL);
305     sigaction_safe(SIGUSR1, &enqueue_sigaction, NULL);
306     sigaction_safe(SIGCONT, &enqueue_sigaction, NULL);
307     sigaction_safe(SIGURG, &enqueue_sigaction, NULL);
308     sigaction_safe(SIGIO, &enqueue_sigaction, NULL);
309     sigaction_safe(SIGPROF, &enqueue_sigaction, NULL);
310     sigaction_safe(SIGVTALRM, &enqueue_sigaction, NULL);
311 #ifdef SIGINFO
312     sigaction_safe(SIGINFO, &enqueue_sigaction, NULL);
313 #endif
314   }
315
316   handle_ctrl_c();
317
318   {
319     struct sigaction sample_sigaction;
320     init_sigaction_with_handler(&sample_sigaction, sample_signal_handler);
321     sigaction_safe(SIGALRM, &sample_sigaction, NULL);
322   }
323
324   /* We don't use SA_IGN here because then the ignore action is inherited
325      by subprocesses, which we don't want. There is a unit test in
326      io.launcher.unix for this. */
327   {
328     struct sigaction ignore_sigaction;
329     init_sigaction_with_handler(&ignore_sigaction, ignore_signal_handler);
330     sigaction_safe(SIGPIPE, &ignore_sigaction, NULL);
331     /* We send SIGUSR2 to the stdin_loop thread to interrupt it on FEP */
332     sigaction_safe(SIGUSR2, &ignore_sigaction, NULL);
333   }
334 }
335
336 /* On Unix, shared fds such as stdin cannot be set to non-blocking mode
337    (http://homepages.tesco.net/J.deBoynePollard/FGA/dont-set-shared-file-descriptors-to-non-blocking-mode.html)
338    so we kludge around this by spawning a thread, which waits on a control pipe
339    for a signal, upon receiving this signal it reads one block of data from
340    stdin and writes it to a data pipe. Upon completion, it writes a 4-byte
341    integer to the size pipe, indicating how much data was written to the data
342    pipe.
343
344    The read end of the size pipe can be set to non-blocking. */
345 extern "C" {
346 int stdin_read;
347 int stdin_write;
348
349 int control_read;
350 int control_write;
351
352 int size_read;
353 int size_write;
354
355 bool stdin_thread_initialized_p = false;
356 THREADHANDLE stdin_thread;
357 pthread_mutex_t stdin_mutex;
358 }
359
360 void safe_close(int fd) {
361   if (close(fd) < 0)
362     fatal_error("error closing fd", errno);
363 }
364
365 bool check_write(int fd, void* data, ssize_t size) {
366   if (write(fd, data, size) == size)
367     return true;
368   if (errno == EINTR)
369     return check_write(fd, data, size);
370   return false;
371 }
372
373 void safe_write(int fd, void* data, ssize_t size) {
374   if (!check_write(fd, data, size))
375     fatal_error("error writing fd", errno);
376 }
377
378 void safe_write_nonblock(int fd, void* data, ssize_t size) {
379   if (!check_write(fd, data, size) && errno != EAGAIN)
380     fatal_error("error writing fd", errno);
381 }
382
383 bool safe_read(int fd, void* data, ssize_t size) {
384   ssize_t bytes = read(fd, data, size);
385   if (bytes < 0) {
386     if (errno == EINTR)
387       return safe_read(fd, data, size);
388     else {
389       fatal_error("error reading fd", errno);
390       return false;
391     }
392   } else
393     return (bytes == size);
394 }
395
396 void* stdin_loop(void* arg) {
397   unsigned char buf[4096];
398   bool loop_running = true;
399
400   sigset_t mask;
401   sigfillset(&mask);
402   sigdelset(&mask, SIGUSR2);
403   sigdelset(&mask, SIGTTIN);
404   sigdelset(&mask, SIGTERM);
405   sigdelset(&mask, SIGQUIT);
406   pthread_sigmask(SIG_SETMASK, &mask, NULL);
407
408   int unused;
409   pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &unused);
410   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &unused);
411
412   while (loop_running) {
413     if (!safe_read(control_read, buf, 1))
414       break;
415
416     if (buf[0] != 'X')
417       fatal_error("stdin_loop: bad data on control fd", buf[0]);
418
419     for (;;) {
420       /* If we fep, the parent thread will grab stdin_mutex and send us
421          SIGUSR2 to interrupt the read() call. */
422       pthread_mutex_lock(&stdin_mutex);
423       pthread_mutex_unlock(&stdin_mutex);
424       ssize_t bytes = read(0, buf, sizeof(buf));
425       if (bytes < 0) {
426         if (errno == EINTR)
427           continue;
428         else {
429           loop_running = false;
430           break;
431         }
432       } else if (bytes >= 0) {
433         safe_write(size_write, &bytes, sizeof(bytes));
434
435         if (!check_write(stdin_write, buf, bytes))
436           loop_running = false;
437         break;
438       }
439     }
440   }
441
442   safe_close(stdin_write);
443   safe_close(control_read);
444
445   return NULL;
446 }
447
448 void open_console() {
449   FACTOR_ASSERT(!stdin_thread_initialized_p);
450   safe_pipe(&control_read, &control_write);
451   safe_pipe(&size_read, &size_write);
452   safe_pipe(&stdin_read, &stdin_write);
453   stdin_thread = start_thread(stdin_loop, NULL);
454   stdin_thread_initialized_p = true;
455   pthread_mutex_init(&stdin_mutex, NULL);
456 }
457
458 /* This method is used to kill the stdin_loop before exiting from factor.
459    A Nvidia driver bug on Linux is the reason this has to be done, see:
460      http://www.nvnews.net/vbulletin/showthread.php?t=164619 */
461 void close_console() {
462   if (stdin_thread_initialized_p) {
463     pthread_cancel(stdin_thread);
464     pthread_join(stdin_thread, 0);
465   }
466 }
467
468 void lock_console() {
469   FACTOR_ASSERT(stdin_thread_initialized_p);
470   /* Lock the stdin_mutex and send the stdin_loop thread a signal to interrupt
471      any read() it has in progress. When the stdin loop iterates again, it will
472      try to lock the same mutex and wait until unlock_console() is called. */
473   pthread_mutex_lock(&stdin_mutex);
474   pthread_kill(stdin_thread, SIGUSR2);
475 }
476
477 void unlock_console() {
478   FACTOR_ASSERT(stdin_thread_initialized_p);
479   pthread_mutex_unlock(&stdin_mutex);
480 }
481
482 void ignore_ctrl_c() {
483   sig_t ret;
484   do {
485     ret = signal(SIGINT, SIG_DFL);
486   } while (ret == SIG_ERR && errno == EINTR);
487 }
488
489 void handle_ctrl_c() {
490   struct sigaction fep_sigaction;
491   init_sigaction_with_handler(&fep_sigaction, fep_signal_handler);
492   sigaction_safe(SIGINT, &fep_sigaction, NULL);
493 }
494
495 void abort() {
496   sig_t ret;
497   do {
498     ret = signal(SIGABRT, SIG_DFL);
499   } while (ret == SIG_ERR && errno == EINTR);
500
501   close_console();
502   ::abort();
503 }
504
505 }