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