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