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