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