]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
Merge branch 'mongodb-changes' of git://github.com/x6j8x/factor
[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(dll *dll, symbol_char *symbol)
51 {
52         void *handle = (dll == NULL ? null_dll : dll->handle);
53         return dlsym(handle,symbol);
54 }
55
56 void factor_vm::ffi_dlclose(dll *dll)
57 {
58         if(dlclose(dll->handle))
59                 general_error(ERROR_FFI,false_object,false_object);
60         dll->handle = NULL;
61 }
62
63 void factor_vm::primitive_existsp()
64 {
65         struct stat sb;
66         char *path = (char *)(untag_check<byte_array>(ctx->pop()) + 1);
67         ctx->push(tag_boolean(stat(path,&sb) >= 0));
68 }
69
70 void factor_vm::move_file(const vm_char *path1, const vm_char *path2)
71 {
72         int ret = 0;
73         do
74         {
75                 ret = rename((path1),(path2));
76         }
77         while(ret < 0 && errno == EINTR);
78
79         if(ret < 0)
80                 general_error(ERROR_IO,tag_fixnum(errno),false_object);
81 }
82
83 segment::segment(cell size_, bool executable_p)
84 {
85         size = size_;
86
87         int pagesize = getpagesize();
88
89         int prot;
90         if(executable_p)
91                 prot = (PROT_READ | PROT_WRITE | PROT_EXEC);
92         else
93                 prot = (PROT_READ | PROT_WRITE);
94
95         char *array = (char *)mmap(NULL,pagesize + size + pagesize,prot,MAP_ANON | MAP_PRIVATE,-1,0);
96         if(array == (char*)-1) out_of_memory();
97
98         if(mprotect(array,pagesize,PROT_NONE) == -1)
99                 fatal_error("Cannot protect low guard page",(cell)array);
100
101         if(mprotect(array + pagesize + size,pagesize,PROT_NONE) == -1)
102                 fatal_error("Cannot protect high guard page",(cell)array);
103
104         start = (cell)(array + pagesize);
105         end = start + size;
106 }
107
108 segment::~segment()
109 {
110         int pagesize = getpagesize();
111         int retval = munmap((void*)(start - pagesize),pagesize + size + pagesize);
112         if(retval)
113                 fatal_error("Segment deallocation failed",0);
114 }
115
116 void factor_vm::dispatch_signal(void *uap, void (handler)())
117 {
118         UAP_STACK_POINTER(uap) = (UAP_STACK_POINTER_TYPE)fix_callstack_top((stack_frame *)UAP_STACK_POINTER(uap));
119         UAP_PROGRAM_COUNTER(uap) = (cell)handler;
120
121         signal_callstack_top = (stack_frame *)UAP_STACK_POINTER(uap);
122 }
123
124 void memory_signal_handler(int signal, siginfo_t *siginfo, void *uap)
125 {
126         factor_vm *vm = current_vm();
127         vm->signal_fault_addr = (cell)siginfo->si_addr;
128         vm->dispatch_signal(uap,factor::memory_signal_handler_impl);
129 }
130
131 void misc_signal_handler(int signal, siginfo_t *siginfo, void *uap)
132 {
133         factor_vm *vm = current_vm();
134         vm->signal_number = signal;
135         vm->dispatch_signal(uap,factor::misc_signal_handler_impl);
136 }
137
138 void fpe_signal_handler(int signal, siginfo_t *siginfo, void *uap)
139 {
140         factor_vm *vm = current_vm();
141         vm->signal_number = signal;
142         vm->signal_fpu_status = fpu_status(uap_fpu_status(uap));
143         uap_clear_fpu_status(uap);
144
145         vm->dispatch_signal(uap,
146                 (siginfo->si_code == FPE_INTDIV || siginfo->si_code == FPE_INTOVF)
147                 ? factor::misc_signal_handler_impl
148                 : factor::fp_signal_handler_impl);
149 }
150
151 static void sigaction_safe(int signum, const struct sigaction *act, struct sigaction *oldact)
152 {
153         int ret;
154         do
155         {
156                 ret = sigaction(signum, act, oldact);
157         }
158         while(ret == -1 && errno == EINTR);
159
160         if(ret == -1)
161                 fatal_error("sigaction failed", 0);
162 }
163
164 void factor_vm::unix_init_signals()
165 {
166         /* OpenBSD doesn't support sigaltstack() if we link against
167         libpthread. See http://redmine.ruby-lang.org/issues/show/1239 */
168
169 #ifndef __OpenBSD__
170         signal_callstack_seg = new segment(callstack_size,false);
171
172         stack_t signal_callstack;
173         signal_callstack.ss_sp = (char *)signal_callstack_seg->start;
174         signal_callstack.ss_size = signal_callstack_seg->size;
175         signal_callstack.ss_flags = 0;
176
177         if(sigaltstack(&signal_callstack,(stack_t *)NULL) < 0)
178                 fatal_error("sigaltstack() failed",0);
179 #endif
180
181         struct sigaction memory_sigaction;
182         struct sigaction misc_sigaction;
183         struct sigaction fpe_sigaction;
184         struct sigaction ignore_sigaction;
185
186         memset(&memory_sigaction,0,sizeof(struct sigaction));
187         sigemptyset(&memory_sigaction.sa_mask);
188         memory_sigaction.sa_sigaction = memory_signal_handler;
189         memory_sigaction.sa_flags = SA_SIGINFO | SA_ONSTACK;
190
191         sigaction_safe(SIGBUS,&memory_sigaction,NULL);
192         sigaction_safe(SIGSEGV,&memory_sigaction,NULL);
193
194         memset(&fpe_sigaction,0,sizeof(struct sigaction));
195         sigemptyset(&fpe_sigaction.sa_mask);
196         fpe_sigaction.sa_sigaction = fpe_signal_handler;
197         fpe_sigaction.sa_flags = SA_SIGINFO | SA_ONSTACK;
198
199         sigaction_safe(SIGFPE,&fpe_sigaction,NULL);
200
201         memset(&misc_sigaction,0,sizeof(struct sigaction));
202         sigemptyset(&misc_sigaction.sa_mask);
203         misc_sigaction.sa_sigaction = misc_signal_handler;
204         misc_sigaction.sa_flags = SA_SIGINFO | SA_ONSTACK;
205
206         sigaction_safe(SIGQUIT,&misc_sigaction,NULL);
207         sigaction_safe(SIGILL,&misc_sigaction,NULL);
208
209         memset(&ignore_sigaction,0,sizeof(struct sigaction));
210         sigemptyset(&ignore_sigaction.sa_mask);
211         ignore_sigaction.sa_handler = SIG_IGN;
212         sigaction_safe(SIGPIPE,&ignore_sigaction,NULL);
213 }
214
215 /* On Unix, shared fds such as stdin cannot be set to non-blocking mode
216 (http://homepages.tesco.net/J.deBoynePollard/FGA/dont-set-shared-file-descriptors-to-non-blocking-mode.html)
217 so we kludge around this by spawning a thread, which waits on a control pipe
218 for a signal, upon receiving this signal it reads one block of data from stdin
219 and writes it to a data pipe. Upon completion, it writes a 4-byte integer to
220 the size pipe, indicating how much data was written to the data pipe.
221
222 The read end of the size pipe can be set to non-blocking. */
223 extern "C" {
224         int stdin_read;
225         int stdin_write;
226
227         int control_read;
228         int control_write;
229
230         int size_read;
231         int size_write;
232 }
233
234 void safe_close(int fd)
235 {
236         if(close(fd) < 0)
237                 fatal_error("error closing fd",errno);
238 }
239
240 bool check_write(int fd, void *data, ssize_t size)
241 {
242         if(write(fd,data,size) == size)
243                 return true;
244         else
245         {
246                 if(errno == EINTR)
247                         return check_write(fd,data,size);
248                 else
249                         return false;
250         }
251 }
252
253 void safe_write(int fd, void *data, ssize_t size)
254 {
255         if(!check_write(fd,data,size))
256                 fatal_error("error writing fd",errno);
257 }
258
259 bool safe_read(int fd, void *data, ssize_t size)
260 {
261         ssize_t bytes = read(fd,data,size);
262         if(bytes < 0)
263         {
264                 if(errno == EINTR)
265                         return safe_read(fd,data,size);
266                 else
267                 {
268                         fatal_error("error reading fd",errno);
269                         return false;
270                 }
271         }
272         else
273                 return (bytes == size);
274 }
275
276 void *stdin_loop(void *arg)
277 {
278         unsigned char buf[4096];
279         bool loop_running = true;
280
281         while(loop_running)
282         {
283                 if(!safe_read(control_read,buf,1))
284                         break;
285
286                 if(buf[0] != 'X')
287                         fatal_error("stdin_loop: bad data on control fd",buf[0]);
288
289                 for(;;)
290                 {
291                         ssize_t bytes = read(0,buf,sizeof(buf));
292                         if(bytes < 0)
293                         {
294                                 if(errno == EINTR)
295                                         continue;
296                                 else
297                                 {
298                                         loop_running = false;
299                                         break;
300                                 }
301                         }
302                         else if(bytes >= 0)
303                         {
304                                 safe_write(size_write,&bytes,sizeof(bytes));
305
306                                 if(!check_write(stdin_write,buf,bytes))
307                                         loop_running = false;
308                                 break;
309                         }
310                 }
311         }
312
313         safe_close(stdin_write);
314         safe_close(control_read);
315
316         return NULL;
317 }
318
319 void open_console()
320 {
321         int filedes[2];
322
323         if(pipe(filedes) < 0)
324                 fatal_error("Error opening control pipe",errno);
325
326         control_read = filedes[0];
327         control_write = filedes[1];
328
329         if(pipe(filedes) < 0)
330                 fatal_error("Error opening size pipe",errno);
331
332         size_read = filedes[0];
333         size_write = filedes[1];
334
335         if(pipe(filedes) < 0)
336                 fatal_error("Error opening stdin pipe",errno);
337
338         stdin_read = filedes[0];
339         stdin_write = filedes[1];
340
341         start_thread(stdin_loop,NULL);
342 }
343
344 VM_C_API void wait_for_stdin()
345 {
346         if(write(control_write,"X",1) != 1)
347         {
348                 if(errno == EINTR)
349                         wait_for_stdin();
350                 else
351                         fatal_error("Error writing control fd",errno);
352         }
353 }
354
355 }