]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
VM: Refactor os-* to Factor style
[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(u64 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);
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   struct sigaction memory_sigaction;
277   struct sigaction synchronous_sigaction;
278   struct sigaction enqueue_sigaction;
279   struct sigaction sample_sigaction;
280   struct sigaction fpe_sigaction;
281   struct sigaction ignore_sigaction;
282
283   init_sigaction_with_handler(&memory_sigaction, memory_signal_handler);
284   sigaction_safe(SIGBUS, &memory_sigaction, NULL);
285   sigaction_safe(SIGSEGV, &memory_sigaction, NULL);
286   sigaction_safe(SIGTRAP, &memory_sigaction, NULL);
287
288   init_sigaction_with_handler(&fpe_sigaction, fpe_signal_handler);
289   sigaction_safe(SIGFPE, &fpe_sigaction, NULL);
290
291   init_sigaction_with_handler(&synchronous_sigaction,
292                               synchronous_signal_handler);
293   sigaction_safe(SIGILL, &synchronous_sigaction, NULL);
294   sigaction_safe(SIGABRT, &synchronous_sigaction, NULL);
295
296   init_sigaction_with_handler(&enqueue_sigaction, enqueue_signal_handler);
297   sigaction_safe(SIGWINCH, &enqueue_sigaction, NULL);
298   sigaction_safe(SIGUSR1, &enqueue_sigaction, NULL);
299   sigaction_safe(SIGCONT, &enqueue_sigaction, NULL);
300   sigaction_safe(SIGURG, &enqueue_sigaction, NULL);
301   sigaction_safe(SIGIO, &enqueue_sigaction, NULL);
302   sigaction_safe(SIGPROF, &enqueue_sigaction, NULL);
303   sigaction_safe(SIGVTALRM, &enqueue_sigaction, NULL);
304 #ifdef SIGINFO
305   sigaction_safe(SIGINFO, &enqueue_sigaction, NULL);
306 #endif
307
308   handle_ctrl_c();
309
310   init_sigaction_with_handler(&sample_sigaction, sample_signal_handler);
311   sigaction_safe(SIGALRM, &sample_sigaction, NULL);
312
313   /* We don't use SA_IGN here because then the ignore action is inherited
314      by subprocesses, which we don't want. There is a unit test in
315      io.launcher.unix for this. */
316   init_sigaction_with_handler(&ignore_sigaction, ignore_signal_handler);
317   sigaction_safe(SIGPIPE, &ignore_sigaction, NULL);
318   /* We send SIGUSR2 to the stdin_loop thread to interrupt it on FEP */
319   sigaction_safe(SIGUSR2, &ignore_sigaction, NULL);
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   else {
355     if (errno == EINTR)
356       return check_write(fd, data, size);
357     else
358       return false;
359   }
360 }
361
362 void safe_write(int fd, void* data, ssize_t size) {
363   if (!check_write(fd, data, size))
364     fatal_error("error writing fd", errno);
365 }
366
367 void safe_write_nonblock(int fd, void* data, ssize_t size) {
368   if (!check_write(fd, data, size) && errno != EAGAIN)
369     fatal_error("error writing fd", errno);
370 }
371
372 bool safe_read(int fd, void* data, ssize_t size) {
373   ssize_t bytes = read(fd, data, size);
374   if (bytes < 0) {
375     if (errno == EINTR)
376       return safe_read(fd, data, size);
377     else {
378       fatal_error("error reading fd", errno);
379       return false;
380     }
381   } else
382     return (bytes == size);
383 }
384
385 void* stdin_loop(void* arg) {
386   unsigned char buf[4096];
387   bool loop_running = true;
388
389   sigset_t mask;
390   sigfillset(&mask);
391   sigdelset(&mask, SIGUSR2);
392   sigdelset(&mask, SIGTTIN);
393   sigdelset(&mask, SIGTERM);
394   sigdelset(&mask, SIGQUIT);
395   pthread_sigmask(SIG_SETMASK, &mask, NULL);
396
397   int unused;
398   pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &unused);
399   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &unused);
400
401   while (loop_running) {
402     if (!safe_read(control_read, buf, 1))
403       break;
404
405     if (buf[0] != 'X')
406       fatal_error("stdin_loop: bad data on control fd", buf[0]);
407
408     for (;;) {
409       /* If we fep, the parent thread will grab stdin_mutex and send us
410          SIGUSR2 to interrupt the read() call. */
411       pthread_mutex_lock(&stdin_mutex);
412       pthread_mutex_unlock(&stdin_mutex);
413       ssize_t bytes = read(0, buf, sizeof(buf));
414       if (bytes < 0) {
415         if (errno == EINTR)
416           continue;
417         else {
418           loop_running = false;
419           break;
420         }
421       } else if (bytes >= 0) {
422         safe_write(size_write, &bytes, sizeof(bytes));
423
424         if (!check_write(stdin_write, buf, bytes))
425           loop_running = false;
426         break;
427       }
428     }
429   }
430
431   safe_close(stdin_write);
432   safe_close(control_read);
433
434   return NULL;
435 }
436
437 void factor_vm::open_console() {
438   FACTOR_ASSERT(!stdin_thread_initialized_p);
439   safe_pipe(&control_read, &control_write);
440   safe_pipe(&size_read, &size_write);
441   safe_pipe(&stdin_read, &stdin_write);
442   stdin_thread = start_thread(stdin_loop, NULL);
443   stdin_thread_initialized_p = true;
444   pthread_mutex_init(&stdin_mutex, NULL);
445 }
446
447 /* This method is used to kill the stdin_loop before exiting from factor.
448    A Nvidia driver bug on Linux is the reason this has to be done, see:
449      http://www.nvnews.net/vbulletin/showthread.php?t=164619 */
450 void factor_vm::close_console() {
451   if (stdin_thread_initialized_p) {
452     pthread_cancel(stdin_thread);
453     pthread_join(stdin_thread, 0);
454   }
455 }
456
457 void factor_vm::lock_console() {
458   FACTOR_ASSERT(stdin_thread_initialized_p);
459   /* Lock the stdin_mutex and send the stdin_loop thread a signal to interrupt
460      any read() it has in progress. When the stdin loop iterates again, it will
461      try to lock the same mutex and wait until unlock_console() is called. */
462   pthread_mutex_lock(&stdin_mutex);
463   pthread_kill(stdin_thread, SIGUSR2);
464 }
465
466 void factor_vm::unlock_console() {
467   FACTOR_ASSERT(stdin_thread_initialized_p);
468   pthread_mutex_unlock(&stdin_mutex);
469 }
470
471 void factor_vm::ignore_ctrl_c() {
472   sig_t ret;
473   do {
474     ret = signal(SIGINT, SIG_DFL);
475   } while (ret == SIG_ERR && errno == EINTR);
476 }
477
478 void factor_vm::handle_ctrl_c() {
479   struct sigaction fep_sigaction;
480   init_sigaction_with_handler(&fep_sigaction, fep_signal_handler);
481   sigaction_safe(SIGINT, &fep_sigaction, NULL);
482 }
483
484 void abort() {
485   sig_t ret;
486   do {
487     ret = signal(SIGABRT, SIG_DFL);
488   } while (ret == SIG_ERR && errno == EINTR);
489
490   factor_vm::close_console();
491   ::abort();
492 }
493
494 }