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