]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
vm: write async signals to pipe for multiplexer
[factor.git] / vm / os-unix.cpp
1 #include "master.hpp"
2
3 namespace factor
4 {
5
6 THREADHANDLE start_thread(void *(*start_routine)(void *),void *args)
7 {
8         pthread_attr_t attr;
9         pthread_t thread;
10         if (pthread_attr_init (&attr) != 0)
11                 fatal_error("pthread_attr_init() failed",0);
12         if (pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE) != 0)
13                 fatal_error("pthread_attr_setdetachstate() failed",0);
14         if (pthread_create (&thread, &attr, start_routine, args) != 0)
15                 fatal_error("pthread_create() failed",0);
16         pthread_attr_destroy(&attr);
17         return thread;
18 }
19
20 static void *null_dll;
21
22 void sleep_nanos(u64 nsec)
23 {
24         timespec ts;
25         timespec ts_rem;
26         int ret;
27         ts.tv_sec = nsec / 1000000000;
28         ts.tv_nsec = nsec % 1000000000;
29         ret = nanosleep(&ts,&ts_rem);
30         while(ret == -1 && errno == EINTR)
31         {
32                 memcpy(&ts, &ts_rem, sizeof(ts));
33                 ret = nanosleep(&ts, &ts_rem);
34         }
35
36         if(ret == -1)
37                 fatal_error("nanosleep failed", 0);
38 }
39
40 void factor_vm::init_ffi()
41 {
42         null_dll = dlopen(NULL,RTLD_LAZY);
43 }
44
45 void factor_vm::ffi_dlopen(dll *dll)
46 {
47         dll->handle = dlopen(alien_offset(dll->path), RTLD_LAZY);
48 }
49
50 void *factor_vm::ffi_dlsym_raw(dll *dll, symbol_char *symbol)
51 {
52         return dlsym(dll ? dll->handle : null_dll, symbol);
53 }
54
55 void *factor_vm::ffi_dlsym(dll *dll, symbol_char *symbol)
56 {
57         return FUNCTION_CODE_POINTER(ffi_dlsym_raw(dll, symbol));
58 }
59
60 #ifdef FACTOR_PPC
61 void *factor_vm::ffi_dlsym_toc(dll *dll, symbol_char *symbol)
62 {
63         return FUNCTION_TOC_POINTER(ffi_dlsym_raw(dll, symbol));
64 }
65 #endif
66
67 void factor_vm::ffi_dlclose(dll *dll)
68 {
69         if(dlclose(dll->handle))
70                 general_error(ERROR_FFI,false_object,false_object);
71         dll->handle = NULL;
72 }
73
74 void factor_vm::primitive_existsp()
75 {
76         struct stat sb;
77         char *path = (char *)(untag_check<byte_array>(ctx->pop()) + 1);
78         ctx->push(tag_boolean(stat(path,&sb) >= 0));
79 }
80
81 void factor_vm::move_file(const vm_char *path1, const vm_char *path2)
82 {
83         int ret = 0;
84         do
85         {
86                 ret = rename((path1),(path2));
87         }
88         while(ret < 0 && errno == EINTR);
89
90         if(ret < 0)
91                 general_error(ERROR_IO,tag_fixnum(errno),false_object);
92 }
93
94 segment::segment(cell size_, bool executable_p)
95 {
96         size = size_;
97
98         int pagesize = getpagesize();
99
100         int prot;
101         if(executable_p)
102                 prot = (PROT_READ | PROT_WRITE | PROT_EXEC);
103         else
104                 prot = (PROT_READ | PROT_WRITE);
105
106         char *array = (char *)mmap(NULL,pagesize + size + pagesize,prot,MAP_ANON | MAP_PRIVATE,-1,0);
107         if(array == (char*)-1) out_of_memory();
108
109         if(mprotect(array,pagesize,PROT_NONE) == -1)
110                 fatal_error("Cannot protect low guard page",(cell)array);
111
112         if(mprotect(array + pagesize + size,pagesize,PROT_NONE) == -1)
113                 fatal_error("Cannot protect high guard page",(cell)array);
114
115         start = (cell)(array + pagesize);
116         end = start + size;
117 }
118
119 segment::~segment()
120 {
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 code_heap::guard_safepoint()
128 {
129         if(mprotect(safepoint_page,getpagesize(),PROT_NONE) == -1)
130                 fatal_error("Cannot protect safepoint guard page",(cell)safepoint_page);
131 }
132
133 void code_heap::unguard_safepoint()
134 {
135         if(mprotect(safepoint_page,getpagesize(),PROT_WRITE) == -1)
136                 fatal_error("Cannot unprotect safepoint guard page",(cell)safepoint_page);
137 }
138
139 void factor_vm::dispatch_signal(void *uap, void (handler)())
140 {
141         dispatch_signal_handler(
142                 (cell*)&UAP_STACK_POINTER(uap),
143                 (cell*)&UAP_PROGRAM_COUNTER(uap),
144                 (cell)FUNCTION_CODE_POINTER(handler)
145         );
146         UAP_SET_TOC_POINTER(uap, (cell)FUNCTION_TOC_POINTER(handler));
147 }
148
149 void factor_vm::start_sampling_profiler_timer()
150 {
151         struct itimerval timer;
152         memset((void*)&timer, 0, sizeof(struct itimerval));
153         timer.it_value.tv_usec = 1000000/samples_per_second;
154         timer.it_interval.tv_usec = 1000000/samples_per_second;
155         setitimer(ITIMER_REAL, &timer, NULL);
156 }
157
158 void factor_vm::end_sampling_profiler_timer()
159 {
160         struct itimerval timer;
161         memset((void*)&timer, 0, sizeof(struct itimerval));
162         setitimer(ITIMER_REAL, &timer, NULL);
163 }
164
165 void memory_signal_handler(int signal, siginfo_t *siginfo, void *uap)
166 {
167         factor_vm *vm = current_vm();
168         vm->signal_fault_addr = (cell)siginfo->si_addr;
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 {
174         factor_vm *vm = current_vm_p();
175         if (vm)
176         {
177                 vm->signal_number = signal;
178                 vm->dispatch_signal(uap,factor::synchronous_signal_handler_impl);
179         } else
180                 fatal_error("Foreign thread received signal", signal);
181 }
182
183 void enqueue_signal_handler(int signal, siginfo_t *siginfo, void *uap)
184 {
185         factor_vm *vm = current_vm_p();
186         if (vm)
187                 vm->safepoint.enqueue_signal(signal);
188         else
189                 fatal_error("Foreign thread received signal", signal);
190 }
191
192 void fep_signal_handler(int signal, siginfo_t *siginfo, void *uap)
193 {
194         factor_vm *vm = current_vm_p();
195         if (vm)
196                 vm->safepoint.enqueue_fep();
197         else
198                 fatal_error("Foreign thread received signal", signal);
199 }
200
201 void sample_signal_handler(int signal, siginfo_t *siginfo, void *uap)
202 {
203         factor_vm *vm = current_vm_p();
204         if (vm)
205                 vm->safepoint.enqueue_samples(1, (cell)UAP_PROGRAM_COUNTER(uap), false);
206         else if (thread_vms.size() == 1) {
207                 factor_vm *the_only_vm = thread_vms.begin()->second;
208                 the_only_vm->safepoint.enqueue_samples(1, (cell)UAP_PROGRAM_COUNTER(uap), true);
209         }
210 }
211
212 void ignore_signal_handler(int signal, siginfo_t *siginfo, void *uap)
213 {
214 }
215
216 void fpe_signal_handler(int signal, siginfo_t *siginfo, void *uap)
217 {
218         factor_vm *vm = current_vm();
219         vm->signal_number = signal;
220         vm->signal_fpu_status = fpu_status(uap_fpu_status(uap));
221         uap_clear_fpu_status(uap);
222
223         vm->dispatch_signal(uap,
224                 (siginfo->si_code == FPE_INTDIV || siginfo->si_code == FPE_INTOVF)
225                 ? factor::synchronous_signal_handler_impl
226                 : factor::fp_signal_handler_impl);
227 }
228
229 static void sigaction_safe(int signum, const struct sigaction *act, struct sigaction *oldact)
230 {
231         int ret;
232         do
233         {
234                 ret = sigaction(signum, act, oldact);
235         }
236         while(ret == -1 && errno == EINTR);
237
238         if(ret == -1)
239                 fatal_error("sigaction failed", 0);
240 }
241
242 static void init_sigaction_with_handler(struct sigaction *act,
243         void (*handler)(int, siginfo_t*, void*))
244 {
245         memset(act, 0, sizeof(struct sigaction));
246         sigemptyset(&act->sa_mask);
247         act->sa_sigaction = handler;
248         act->sa_flags = SA_SIGINFO | SA_ONSTACK;
249 }
250
251 static void safe_pipe(int *in, int *out)
252 {
253         int filedes[2];
254
255         if(pipe(filedes) < 0)
256                 fatal_error("Error opening pipe",errno);
257
258         *in = filedes[0];
259         *out = filedes[1];
260
261         if(fcntl(*in,F_SETFD,FD_CLOEXEC) < 0)
262                 fatal_error("Error with fcntl",errno);
263
264         if(fcntl(*out,F_SETFD,FD_CLOEXEC) < 0)
265                 fatal_error("Error with fcntl",errno);
266 }
267
268 static void init_signal_pipe(factor_vm *vm)
269 {
270         safe_pipe(&vm->signal_pipe_input, &vm->signal_pipe_output);
271         vm->special_objects[OBJ_SIGNAL_PIPE] = tag_fixnum(vm->signal_pipe_output);
272 }
273
274 void factor_vm::unix_init_signals()
275 {
276         init_signal_pipe(this);
277
278         signal_callstack_seg = new segment(callstack_size,false);
279
280         stack_t signal_callstack;
281         signal_callstack.ss_sp = (char *)signal_callstack_seg->start;
282         signal_callstack.ss_size = signal_callstack_seg->size;
283         signal_callstack.ss_flags = 0;
284
285         if(sigaltstack(&signal_callstack,(stack_t *)NULL) < 0)
286                 fatal_error("sigaltstack() failed",0);
287
288         struct sigaction memory_sigaction;
289         struct sigaction synchronous_sigaction;
290         struct sigaction enqueue_sigaction;
291         struct sigaction fep_sigaction;
292         struct sigaction sample_sigaction;
293         struct sigaction fpe_sigaction;
294         struct sigaction ignore_sigaction;
295
296         init_sigaction_with_handler(&memory_sigaction, memory_signal_handler);
297         sigaction_safe(SIGBUS,&memory_sigaction,NULL);
298         sigaction_safe(SIGSEGV,&memory_sigaction,NULL);
299         sigaction_safe(SIGTRAP,&memory_sigaction,NULL);
300
301         init_sigaction_with_handler(&fpe_sigaction, fpe_signal_handler);
302         sigaction_safe(SIGFPE,&fpe_sigaction,NULL);
303
304         init_sigaction_with_handler(&synchronous_sigaction, synchronous_signal_handler);
305         sigaction_safe(SIGILL,&synchronous_sigaction,NULL);
306         sigaction_safe(SIGABRT,&synchronous_sigaction,NULL);
307
308         init_sigaction_with_handler(&enqueue_sigaction, enqueue_signal_handler);
309         sigaction_safe(SIGUSR1,&enqueue_sigaction,NULL);
310         sigaction_safe(SIGUSR2,&enqueue_sigaction,NULL);
311         sigaction_safe(SIGWINCH,&enqueue_sigaction,NULL);
312 #ifdef SIGINFO
313         sigaction_safe(SIGINFO,&enqueue_sigaction,NULL);
314 #endif
315
316         init_sigaction_with_handler(&fep_sigaction, fep_signal_handler);
317         sigaction_safe(SIGQUIT,&fep_sigaction,NULL);
318         sigaction_safe(SIGINT,&fep_sigaction,NULL);
319
320         init_sigaction_with_handler(&sample_sigaction, sample_signal_handler);
321         sigaction_safe(SIGALRM,&sample_sigaction,NULL);
322
323         /* We don't use SA_IGN here because then the ignore action is inherited
324         by subprocesses, which we don't want. There is a unit test in
325         io.launcher.unix for this. */
326         init_sigaction_with_handler(&ignore_sigaction, ignore_signal_handler);
327         sigaction_safe(SIGPIPE,&ignore_sigaction,NULL);
328 }
329
330 /* On Unix, shared fds such as stdin cannot be set to non-blocking mode
331 (http://homepages.tesco.net/J.deBoynePollard/FGA/dont-set-shared-file-descriptors-to-non-blocking-mode.html)
332 so we kludge around this by spawning a thread, which waits on a control pipe
333 for a signal, upon receiving this signal it reads one block of data from stdin
334 and writes it to a data pipe. Upon completion, it writes a 4-byte integer to
335 the size pipe, indicating how much data was written to the data pipe.
336
337 The read end of the size pipe can be set to non-blocking. */
338 extern "C" {
339         int stdin_read;
340         int stdin_write;
341
342         int control_read;
343         int control_write;
344
345         int size_read;
346         int size_write;
347 }
348
349 void safe_close(int fd)
350 {
351         if(close(fd) < 0)
352                 fatal_error("error closing fd",errno);
353 }
354
355 bool check_write(int fd, void *data, ssize_t size)
356 {
357         if(write(fd,data,size) == size)
358                 return true;
359         else
360         {
361                 if(errno == EINTR)
362                         return check_write(fd,data,size);
363                 else
364                         return false;
365         }
366 }
367
368 void safe_write(int fd, void *data, ssize_t size)
369 {
370         if(!check_write(fd,data,size))
371                 fatal_error("error writing fd",errno);
372 }
373
374 bool safe_read(int fd, void *data, ssize_t size)
375 {
376         ssize_t bytes = read(fd,data,size);
377         if(bytes < 0)
378         {
379                 if(errno == EINTR)
380                         return safe_read(fd,data,size);
381                 else
382                 {
383                         fatal_error("error reading fd",errno);
384                         return false;
385                 }
386         }
387         else
388                 return (bytes == size);
389 }
390
391 void *stdin_loop(void *arg)
392 {
393         unsigned char buf[4096];
394         bool loop_running = true;
395
396         sigset_t mask;
397         sigfillset(&mask);
398         pthread_sigmask(SIG_BLOCK, &mask, NULL);
399
400         while(loop_running)
401         {
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                 {
410                         ssize_t bytes = read(0,buf,sizeof(buf));
411                         if(bytes < 0)
412                         {
413                                 if(errno == EINTR)
414                                         continue;
415                                 else
416                                 {
417                                         loop_running = false;
418                                         break;
419                                 }
420                         }
421                         else if(bytes >= 0)
422                         {
423                                 safe_write(size_write,&bytes,sizeof(bytes));
424
425                                 if(!check_write(stdin_write,buf,bytes))
426                                         loop_running = false;
427                                 break;
428                         }
429                 }
430         }
431
432         safe_close(stdin_write);
433         safe_close(control_read);
434
435         return NULL;
436 }
437
438 void open_console()
439 {
440         safe_pipe(&control_read,&control_write);
441         safe_pipe(&size_read,&size_write);
442         safe_pipe(&stdin_read,&stdin_write);
443         start_thread(stdin_loop,NULL);
444 }
445
446 void safepoint_state::report_signal(int fd) volatile
447 {
448         int signal = (int)atomic::load(&queued_signal);
449         if (signal != 0)
450         {
451                 safe_write(fd, &signal, sizeof(int));
452                 atomic::store(&queued_signal, 0);
453         }
454 }
455
456 }