]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
vm: Simplify strerror code.
[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 void* factor_vm::ffi_dlsym_raw(dll* dll, symbol_char* symbol) {
43   return dlsym(dll ? dll->handle : null_dll, symbol);
44 }
45
46 void* 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 void* 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 segment::segment(cell size_, bool executable_p) {
79   size = size_;
80
81   int pagesize = getpagesize();
82
83   int prot;
84   if (executable_p)
85     prot = (PROT_READ | PROT_WRITE | PROT_EXEC);
86   else
87     prot = (PROT_READ | PROT_WRITE);
88
89   char* array = (char*)mmap(NULL, pagesize + size + pagesize, prot,
90                             MAP_ANON | MAP_PRIVATE, -1, 0);
91   if (array == (char*)- 1)
92     out_of_memory();
93
94   if (mprotect(array, pagesize, PROT_NONE) == -1)
95     fatal_error("Cannot protect low guard page", (cell)array);
96
97   if (mprotect(array + pagesize + size, pagesize, PROT_NONE) == -1)
98     fatal_error("Cannot protect high guard page", (cell)array);
99
100   start = (cell)(array + pagesize);
101   end = start + size;
102 }
103
104 segment::~segment() {
105   int pagesize = getpagesize();
106   int retval = munmap((void*)(start - pagesize), pagesize + size + pagesize);
107   if (retval)
108     fatal_error("Segment deallocation failed", 0);
109 }
110
111 void code_heap::guard_safepoint() {
112   if (mprotect(safepoint_page, getpagesize(), PROT_NONE) == -1)
113     fatal_error("Cannot protect safepoint guard page", (cell)safepoint_page);
114 }
115
116 void code_heap::unguard_safepoint() {
117   if (mprotect(safepoint_page, getpagesize(), PROT_WRITE) == -1)
118     fatal_error("Cannot unprotect safepoint guard page", (cell)safepoint_page);
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   factor_vm* vm = current_vm();
144   vm->verify_memory_protection_error((cell)siginfo->si_addr);
145   vm->signal_fault_addr = (cell)siginfo->si_addr;
146   vm->signal_fault_pc = (cell)UAP_PROGRAM_COUNTER(uap);
147   vm->dispatch_signal(uap, factor::memory_signal_handler_impl);
148 }
149
150 void synchronous_signal_handler(int signal, siginfo_t* siginfo, void* uap) {
151   if (factor_vm::fatal_erroring_p)
152     return;
153
154   factor_vm* vm = current_vm_p();
155   if (vm) {
156     vm->signal_number = signal;
157     vm->dispatch_signal(uap, factor::synchronous_signal_handler_impl);
158   } else
159     fatal_error("Foreign thread received signal", signal);
160 }
161
162 void safe_write_nonblock(int fd, void* data, ssize_t size);
163
164 static void enqueue_signal(factor_vm* vm, int signal) {
165   if (vm->signal_pipe_output != 0)
166     safe_write_nonblock(vm->signal_pipe_output, &signal, sizeof(int));
167 }
168
169 void enqueue_signal_handler(int signal, siginfo_t* siginfo, void* uap) {
170   if (factor_vm::fatal_erroring_p)
171     return;
172
173   factor_vm* vm = current_vm_p();
174   if (vm)
175     enqueue_signal(vm, signal);
176 }
177
178 void fep_signal_handler(int signal, siginfo_t* siginfo, void* uap) {
179   if (factor_vm::fatal_erroring_p)
180     return;
181
182   factor_vm* vm = current_vm_p();
183   if (vm) {
184     vm->safepoint.enqueue_fep(vm);
185     enqueue_signal(vm, signal);
186   } else
187     fatal_error("Foreign thread received signal", signal);
188 }
189
190 void sample_signal_handler(int signal, siginfo_t* siginfo, void* uap) {
191   factor_vm* vm = current_vm_p();
192   bool foreign_thread = false;
193   if (vm == NULL) {
194     foreign_thread = true;
195     vm = thread_vms.begin()->second;
196   }
197   if (atomic::load(&vm->sampling_profiler_p))
198     vm->safepoint.enqueue_samples(vm, 1, (cell)UAP_PROGRAM_COUNTER(uap),
199                                   foreign_thread);
200   else if (!foreign_thread)
201     enqueue_signal(vm, signal);
202 }
203
204 void ignore_signal_handler(int signal, siginfo_t* siginfo, void* uap) {}
205
206 void fpe_signal_handler(int signal, siginfo_t* siginfo, void* uap) {
207   factor_vm* vm = current_vm();
208   vm->signal_number = signal;
209   vm->signal_fpu_status = fpu_status(uap_fpu_status(uap));
210   uap_clear_fpu_status(uap);
211
212   vm->dispatch_signal(
213       uap, (siginfo->si_code == FPE_INTDIV || siginfo->si_code == FPE_INTOVF)
214                ? factor::synchronous_signal_handler_impl
215                : factor::fp_signal_handler_impl);
216 }
217
218 static void sigaction_safe(int signum, const struct sigaction* act,
219                            struct sigaction* oldact) {
220   int ret;
221   do {
222     ret = sigaction(signum, act, oldact);
223   } while (ret == -1 && errno == EINTR);
224
225   if (ret == -1)
226     fatal_error("sigaction failed", errno);
227 }
228
229 static void init_sigaction_with_handler(struct sigaction* act,
230                                         void (*handler)(int, siginfo_t*,
231                                                         void*)) {
232   memset(act, 0, sizeof(struct sigaction));
233   sigemptyset(&act->sa_mask);
234   act->sa_sigaction = handler;
235   act->sa_flags = SA_SIGINFO | SA_ONSTACK;
236 }
237
238 static void safe_pipe(int* in, int* out) {
239   int filedes[2];
240
241   if (pipe(filedes) < 0)
242     fatal_error("Error opening pipe", errno);
243
244   *in = filedes[0];
245   *out = filedes[1];
246
247   if (fcntl(*in, F_SETFD, FD_CLOEXEC) < 0)
248     fatal_error("Error with fcntl", errno);
249
250   if (fcntl(*out, F_SETFD, FD_CLOEXEC) < 0)
251     fatal_error("Error with fcntl", errno);
252 }
253
254 static void init_signal_pipe(factor_vm* vm) {
255   safe_pipe(&vm->signal_pipe_input, &vm->signal_pipe_output);
256
257   if (fcntl(vm->signal_pipe_output, F_SETFL, O_NONBLOCK) < 0)
258     fatal_error("Error with fcntl", errno);
259
260   vm->special_objects[OBJ_SIGNAL_PIPE] = tag_fixnum(vm->signal_pipe_input);
261 }
262
263 void factor_vm::unix_init_signals() {
264   init_signal_pipe(this);
265
266   signal_callstack_seg = new segment(callstack_size, false);
267
268   stack_t signal_callstack;
269   signal_callstack.ss_sp = (char*)signal_callstack_seg->start;
270   signal_callstack.ss_size = signal_callstack_seg->size;
271   signal_callstack.ss_flags = 0;
272
273   if (sigaltstack(&signal_callstack, (stack_t*)NULL) < 0)
274     fatal_error("sigaltstack() failed", 0);
275
276   {
277     struct sigaction memory_sigaction;
278     init_sigaction_with_handler(&memory_sigaction, memory_signal_handler);
279     sigaction_safe(SIGBUS, &memory_sigaction, NULL);
280     sigaction_safe(SIGSEGV, &memory_sigaction, NULL);
281     sigaction_safe(SIGTRAP, &memory_sigaction, NULL);
282   }
283
284   {
285     struct sigaction fpe_sigaction;
286     init_sigaction_with_handler(&fpe_sigaction, fpe_signal_handler);
287     sigaction_safe(SIGFPE, &fpe_sigaction, NULL);
288   }
289
290   {
291     struct sigaction synchronous_sigaction;
292     init_sigaction_with_handler(&synchronous_sigaction,
293                                 synchronous_signal_handler);
294     sigaction_safe(SIGILL, &synchronous_sigaction, NULL);
295     sigaction_safe(SIGABRT, &synchronous_sigaction, NULL);
296   }
297
298   {
299     struct sigaction enqueue_sigaction;
300     init_sigaction_with_handler(&enqueue_sigaction, enqueue_signal_handler);
301     sigaction_safe(SIGWINCH, &enqueue_sigaction, NULL);
302     sigaction_safe(SIGUSR1, &enqueue_sigaction, NULL);
303     sigaction_safe(SIGCONT, &enqueue_sigaction, NULL);
304     sigaction_safe(SIGURG, &enqueue_sigaction, NULL);
305     sigaction_safe(SIGIO, &enqueue_sigaction, NULL);
306     sigaction_safe(SIGPROF, &enqueue_sigaction, NULL);
307     sigaction_safe(SIGVTALRM, &enqueue_sigaction, NULL);
308 #ifdef SIGINFO
309     sigaction_safe(SIGINFO, &enqueue_sigaction, NULL);
310 #endif
311   }
312
313   handle_ctrl_c();
314
315   {
316     struct sigaction sample_sigaction;
317     init_sigaction_with_handler(&sample_sigaction, sample_signal_handler);
318     sigaction_safe(SIGALRM, &sample_sigaction, NULL);
319   }
320
321   /* We don't use SA_IGN here because then the ignore action is inherited
322      by subprocesses, which we don't want. There is a unit test in
323      io.launcher.unix for this. */
324   {
325     struct sigaction ignore_sigaction;
326     init_sigaction_with_handler(&ignore_sigaction, ignore_signal_handler);
327     sigaction_safe(SIGPIPE, &ignore_sigaction, NULL);
328     /* We send SIGUSR2 to the stdin_loop thread to interrupt it on FEP */
329     sigaction_safe(SIGUSR2, &ignore_sigaction, NULL);
330   }
331 }
332
333 /* On Unix, shared fds such as stdin cannot be set to non-blocking mode
334    (http://homepages.tesco.net/J.deBoynePollard/FGA/dont-set-shared-file-descriptors-to-non-blocking-mode.html)
335    so we kludge around this by spawning a thread, which waits on a control pipe
336    for a signal, upon receiving this signal it reads one block of data from
337    stdin and writes it to a data pipe. Upon completion, it writes a 4-byte
338    integer to the size pipe, indicating how much data was written to the data
339    pipe.
340
341    The read end of the size pipe can be set to non-blocking. */
342 extern "C" {
343 int stdin_read;
344 int stdin_write;
345
346 int control_read;
347 int control_write;
348
349 int size_read;
350 int size_write;
351
352 bool stdin_thread_initialized_p = false;
353 THREADHANDLE stdin_thread;
354 pthread_mutex_t stdin_mutex;
355 }
356
357 void safe_close(int fd) {
358   if (close(fd) < 0)
359     fatal_error("error closing fd", errno);
360 }
361
362 bool check_write(int fd, void* data, ssize_t size) {
363   if (write(fd, data, size) == size)
364     return true;
365   else {
366     if (errno == EINTR)
367       return check_write(fd, data, size);
368     else
369       return false;
370   }
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 factor_vm::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 factor_vm::close_console() {
462   if (stdin_thread_initialized_p) {
463     pthread_cancel(stdin_thread);
464     pthread_join(stdin_thread, 0);
465   }
466 }
467
468 void factor_vm::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 factor_vm::unlock_console() {
478   FACTOR_ASSERT(stdin_thread_initialized_p);
479   pthread_mutex_unlock(&stdin_mutex);
480 }
481
482 void factor_vm::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 factor_vm::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   factor_vm::close_console();
502   ::abort();
503 }
504
505 }