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