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