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