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