]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
vm: remove half-assed signal queueing scaffolding
[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::enqueue_safepoint_signal(cell signal)
150 {
151         /* to be implemented, see #297
152         code->guard_safepoint();
153         */
154 }
155
156 void memory_signal_handler(int signal, siginfo_t *siginfo, void *uap)
157 {
158         factor_vm *vm = current_vm();
159         vm->signal_fault_addr = (cell)siginfo->si_addr;
160         vm->dispatch_signal(uap,factor::memory_signal_handler_impl);
161 }
162
163 void synchronous_signal_handler(int signal, siginfo_t *siginfo, void *uap)
164 {
165         factor_vm *vm = current_vm_p();
166         if (vm)
167         {
168                 vm->signal_number = signal;
169                 vm->dispatch_signal(uap,factor::synchronous_signal_handler_impl);
170         } else
171                 fatal_error("Foreign thread received signal ", signal);
172 }
173
174 void enqueue_signal_handler(int signal, siginfo_t *siginfo, void *uap)
175 {
176         factor_vm *vm = current_vm_p();
177         if (vm)
178                 vm->enqueue_safepoint_signal(signal);
179         else
180                 fatal_error("Foreign thread received signal ", signal);
181 }
182
183 void fep_signal_handler(int signal, siginfo_t *siginfo, void *uap)
184 {
185         factor_vm *vm = current_vm_p();
186         if (vm)
187                 vm->enqueue_safepoint_fep();
188         else
189                 fatal_error("Foreign thread received signal ", signal);
190 }
191
192 void sample_signal_handler(int signal, siginfo_t *siginfo, void *uap)
193 {
194         factor_vm *vm = current_vm_p();
195         if (vm)
196                 vm->enqueue_safepoint_sample();
197         else
198                 fatal_error("Foreign thread received signal ", signal);
199 }
200
201 void ignore_signal_handler(int signal, siginfo_t *siginfo, void *uap)
202 {
203 }
204
205 void fpe_signal_handler(int signal, siginfo_t *siginfo, void *uap)
206 {
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(uap,
213                 (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, struct sigaction *oldact)
219 {
220         int ret;
221         do
222         {
223                 ret = sigaction(signum, act, oldact);
224         }
225         while(ret == -1 && errno == EINTR);
226
227         if(ret == -1)
228                 fatal_error("sigaction failed", 0);
229 }
230
231 static void init_sigaction_with_handler(struct sigaction *act,
232         void (*handler)(int, siginfo_t*, void*))
233 {
234         memset(act, 0, sizeof(struct sigaction));
235         sigemptyset(&act->sa_mask);
236         act->sa_sigaction = handler;
237         act->sa_flags = SA_SIGINFO | SA_ONSTACK;
238 }
239
240 void factor_vm::unix_init_signals()
241 {
242         /* OpenBSD doesn't support sigaltstack() if we link against
243         libpthread. See http://redmine.ruby-lang.org/issues/show/1239 */
244
245 #ifndef __OpenBSD__
246         signal_callstack_seg = new segment(callstack_size,false);
247
248         stack_t signal_callstack;
249         signal_callstack.ss_sp = (char *)signal_callstack_seg->start;
250         signal_callstack.ss_size = signal_callstack_seg->size;
251         signal_callstack.ss_flags = 0;
252
253         if(sigaltstack(&signal_callstack,(stack_t *)NULL) < 0)
254                 fatal_error("sigaltstack() failed",0);
255 #endif
256
257         struct sigaction memory_sigaction;
258         struct sigaction synchronous_sigaction;
259         struct sigaction enqueue_sigaction;
260         struct sigaction fep_sigaction;
261         struct sigaction sample_sigaction;
262         struct sigaction fpe_sigaction;
263         struct sigaction ignore_sigaction;
264
265         init_sigaction_with_handler(&memory_sigaction, memory_signal_handler);
266         sigaction_safe(SIGBUS,&memory_sigaction,NULL);
267         sigaction_safe(SIGSEGV,&memory_sigaction,NULL);
268         sigaction_safe(SIGTRAP,&memory_sigaction,NULL);
269
270         init_sigaction_with_handler(&fpe_sigaction, fpe_signal_handler);
271         sigaction_safe(SIGFPE,&fpe_sigaction,NULL);
272
273         init_sigaction_with_handler(&synchronous_sigaction, synchronous_signal_handler);
274         sigaction_safe(SIGILL,&synchronous_sigaction,NULL);
275         sigaction_safe(SIGABRT,&synchronous_sigaction,NULL);
276
277         init_sigaction_with_handler(&enqueue_sigaction, enqueue_signal_handler);
278         sigaction_safe(SIGUSR1,&enqueue_sigaction,NULL);
279         sigaction_safe(SIGUSR2,&enqueue_sigaction,NULL);
280         sigaction_safe(SIGWINCH,&enqueue_sigaction,NULL);
281 #ifdef SIGINFO
282         sigaction_safe(SIGINFO,&enqueue_sigaction,NULL);
283 #endif
284
285         init_sigaction_with_handler(&fep_sigaction, fep_signal_handler);
286         sigaction_safe(SIGQUIT,&fep_sigaction,NULL);
287         sigaction_safe(SIGINT,&fep_sigaction,NULL);
288
289         init_sigaction_with_handler(&sample_sigaction, sample_signal_handler);
290         sigaction_safe(SIGALRM,&sample_sigaction,NULL);
291
292         /* We don't use SA_IGN here because then the ignore action is inherited
293         by subprocesses, which we don't want. There is a unit test in
294         io.launcher.unix for this. */
295         init_sigaction_with_handler(&ignore_sigaction, ignore_signal_handler);
296         sigaction_safe(SIGPIPE,&ignore_sigaction,NULL);
297 }
298
299 /* On Unix, shared fds such as stdin cannot be set to non-blocking mode
300 (http://homepages.tesco.net/J.deBoynePollard/FGA/dont-set-shared-file-descriptors-to-non-blocking-mode.html)
301 so we kludge around this by spawning a thread, which waits on a control pipe
302 for a signal, upon receiving this signal it reads one block of data from stdin
303 and writes it to a data pipe. Upon completion, it writes a 4-byte integer to
304 the size pipe, indicating how much data was written to the data pipe.
305
306 The read end of the size pipe can be set to non-blocking. */
307 extern "C" {
308         int stdin_read;
309         int stdin_write;
310
311         int control_read;
312         int control_write;
313
314         int size_read;
315         int size_write;
316 }
317
318 void safe_close(int fd)
319 {
320         if(close(fd) < 0)
321                 fatal_error("error closing fd",errno);
322 }
323
324 bool check_write(int fd, void *data, ssize_t size)
325 {
326         if(write(fd,data,size) == size)
327                 return true;
328         else
329         {
330                 if(errno == EINTR)
331                         return check_write(fd,data,size);
332                 else
333                         return false;
334         }
335 }
336
337 void safe_write(int fd, void *data, ssize_t size)
338 {
339         if(!check_write(fd,data,size))
340                 fatal_error("error writing fd",errno);
341 }
342
343 bool safe_read(int fd, void *data, ssize_t size)
344 {
345         ssize_t bytes = read(fd,data,size);
346         if(bytes < 0)
347         {
348                 if(errno == EINTR)
349                         return safe_read(fd,data,size);
350                 else
351                 {
352                         fatal_error("error reading fd",errno);
353                         return false;
354                 }
355         }
356         else
357                 return (bytes == size);
358 }
359
360 void *stdin_loop(void *arg)
361 {
362         unsigned char buf[4096];
363         bool loop_running = true;
364
365         sigset_t mask;
366         sigfillset(&mask);
367         pthread_sigmask(SIG_BLOCK, &mask, NULL);
368
369         while(loop_running)
370         {
371                 if(!safe_read(control_read,buf,1))
372                         break;
373
374                 if(buf[0] != 'X')
375                         fatal_error("stdin_loop: bad data on control fd",buf[0]);
376
377                 for(;;)
378                 {
379                         ssize_t bytes = read(0,buf,sizeof(buf));
380                         if(bytes < 0)
381                         {
382                                 if(errno == EINTR)
383                                         continue;
384                                 else
385                                 {
386                                         loop_running = false;
387                                         break;
388                                 }
389                         }
390                         else if(bytes >= 0)
391                         {
392                                 safe_write(size_write,&bytes,sizeof(bytes));
393
394                                 if(!check_write(stdin_write,buf,bytes))
395                                         loop_running = false;
396                                 break;
397                         }
398                 }
399         }
400
401         safe_close(stdin_write);
402         safe_close(control_read);
403
404         return NULL;
405 }
406
407 void safe_pipe(int *in, int *out)
408 {
409         int filedes[2];
410
411         if(pipe(filedes) < 0)
412                 fatal_error("Error opening pipe",errno);
413
414         *in = filedes[0];
415         *out = filedes[1];
416
417         if(fcntl(*in,F_SETFD,FD_CLOEXEC) < 0)
418                 fatal_error("Error with fcntl",errno);
419
420         if(fcntl(*out,F_SETFD,FD_CLOEXEC) < 0)
421                 fatal_error("Error with fcntl",errno);
422 }
423
424 void open_console()
425 {
426         safe_pipe(&control_read,&control_write);
427         safe_pipe(&size_read,&size_write);
428         safe_pipe(&stdin_read,&stdin_write);
429         start_thread(stdin_loop,NULL);
430 }
431
432 }