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