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