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