]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
Removed VM_PTR macros. All builds reentrant by default
[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 s64 current_micros()
44 {
45         struct timeval t;
46         gettimeofday(&t,NULL);
47         return (s64)t.tv_sec * 1000000 + t.tv_usec;
48 }
49
50 void sleep_micros(cell usec)
51 {
52         usleep(usec);
53 }
54
55 void factor_vm::init_ffi()
56 {
57         /* NULL_DLL is "libfactor.dylib" for OS X and NULL for generic unix */
58         null_dll = dlopen(NULL_DLL,RTLD_LAZY);
59 }
60
61 void factor_vm::ffi_dlopen(dll *dll)
62 {
63         dll->dll = dlopen(alien_offset(dll->path), RTLD_LAZY);
64 }
65
66 void *factor_vm::ffi_dlsym(dll *dll, symbol_char *symbol)
67 {
68         void *handle = (dll == NULL ? null_dll : dll->dll);
69         return dlsym(handle,symbol);
70 }
71
72 void factor_vm::ffi_dlclose(dll *dll)
73 {
74         if(dlclose(dll->dll))
75                 general_error(ERROR_FFI,F,F,NULL);
76         dll->dll = NULL;
77 }
78
79 void factor_vm::primitive_existsp()
80 {
81         struct stat sb;
82         char *path = (char *)(untag_check<byte_array>(dpop()) + 1);
83         box_boolean(stat(path,&sb) >= 0);
84 }
85
86 segment::segment(cell size_)
87 {
88         size = size_;
89
90         int pagesize = getpagesize();
91
92         char *array = (char *)mmap(NULL,pagesize + size + pagesize,
93                 PROT_READ | PROT_WRITE | PROT_EXEC,
94                 MAP_ANON | MAP_PRIVATE,-1,0);
95
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 stack_frame *factor_vm::uap_stack_pointer(void *uap)
117 {
118         /* There is a race condition here, but in practice a signal
119         delivered during stack frame setup/teardown or while transitioning
120         from Factor to C is a sign of things seriously gone wrong, not just
121         a divide by zero or stack underflow in the listener */
122         if(in_code_heap_p(UAP_PROGRAM_COUNTER(uap)))
123         {
124                 stack_frame *ptr = (stack_frame *)ucontext_stack_pointer(uap);
125                 if(!ptr)
126                         critical_error("Invalid uap",(cell)uap);
127                 return ptr;
128         }
129         else
130                 return NULL;
131 }
132
133 void factor_vm::memory_signal_handler(int signal, siginfo_t *siginfo, void *uap)
134 {
135         signal_fault_addr = (cell)siginfo->si_addr;
136         signal_callstack_top = uap_stack_pointer(uap);
137         UAP_PROGRAM_COUNTER(uap) = (cell)factor::memory_signal_handler_impl;
138 }
139
140 void memory_signal_handler(int signal, siginfo_t *siginfo, void *uap)
141 {
142         tls_vm()->memory_signal_handler(signal,siginfo,uap);
143 }
144
145 void factor_vm::misc_signal_handler(int signal, siginfo_t *siginfo, void *uap)
146 {
147         signal_number = signal;
148         signal_callstack_top = uap_stack_pointer(uap);
149         UAP_PROGRAM_COUNTER(uap) = (cell)factor::misc_signal_handler_impl;
150 }
151
152 void misc_signal_handler(int signal, siginfo_t *siginfo, void *uap)
153 {
154         tls_vm()->misc_signal_handler(signal,siginfo,uap);
155 }
156
157 void factor_vm::fpe_signal_handler(int signal, siginfo_t *siginfo, void *uap)
158 {
159         signal_number = signal;
160         signal_callstack_top = uap_stack_pointer(uap);
161         signal_fpu_status = fpu_status(uap_fpu_status(uap));
162         uap_clear_fpu_status(uap);
163         UAP_PROGRAM_COUNTER(uap) =
164                 (siginfo->si_code == FPE_INTDIV || siginfo->si_code == FPE_INTOVF)
165                 ? (cell)factor::misc_signal_handler_impl
166                 : (cell)factor::fp_signal_handler_impl;
167 }
168
169 void fpe_signal_handler(int signal, siginfo_t *siginfo, void *uap)
170 {
171         tls_vm()->fpe_signal_handler(signal, siginfo, uap);
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(SIGABRT,&misc_sigaction,NULL);
215         sigaction_safe(SIGQUIT,&misc_sigaction,NULL);
216         sigaction_safe(SIGILL,&misc_sigaction,NULL);
217
218         memset(&ignore_sigaction,0,sizeof(struct sigaction));
219         sigemptyset(&ignore_sigaction.sa_mask);
220         ignore_sigaction.sa_handler = SIG_IGN;
221         sigaction_safe(SIGPIPE,&ignore_sigaction,NULL);
222 }
223
224 /* On Unix, shared fds such as stdin cannot be set to non-blocking mode
225 (http://homepages.tesco.net/J.deBoynePollard/FGA/dont-set-shared-file-descriptors-to-non-blocking-mode.html)
226 so we kludge around this by spawning a thread, which waits on a control pipe
227 for a signal, upon receiving this signal it reads one block of data from stdin
228 and writes it to a data pipe. Upon completion, it writes a 4-byte integer to
229 the size pipe, indicating how much data was written to the data pipe.
230
231 The read end of the size pipe can be set to non-blocking. */
232 extern "C" {
233         int stdin_read;
234         int stdin_write;
235
236         int control_read;
237         int control_write;
238
239         int size_read;
240         int size_write;
241 }
242
243 void safe_close(int fd)
244 {
245         if(close(fd) < 0)
246                 fatal_error("error closing fd",errno);
247 }
248
249 bool check_write(int fd, void *data, ssize_t size)
250 {
251         if(write(fd,data,size) == size)
252                 return true;
253         else
254         {
255                 if(errno == EINTR)
256                         return check_write(fd,data,size);
257                 else
258                         return false;
259         }
260 }
261
262 void safe_write(int fd, void *data, ssize_t size)
263 {
264         if(!check_write(fd,data,size))
265                 fatal_error("error writing fd",errno);
266 }
267
268 bool safe_read(int fd, void *data, ssize_t size)
269 {
270         ssize_t bytes = read(fd,data,size);
271         if(bytes < 0)
272         {
273                 if(errno == EINTR)
274                         return safe_read(fd,data,size);
275                 else
276                 {
277                         fatal_error("error reading fd",errno);
278                         return false;
279                 }
280         }
281         else
282                 return (bytes == size);
283 }
284
285 void *stdin_loop(void *arg)
286 {
287         unsigned char buf[4096];
288         bool loop_running = true;
289
290         while(loop_running)
291         {
292                 if(!safe_read(control_read,buf,1))
293                         break;
294
295                 if(buf[0] != 'X')
296                         fatal_error("stdin_loop: bad data on control fd",buf[0]);
297
298                 for(;;)
299                 {
300                         ssize_t bytes = read(0,buf,sizeof(buf));
301                         if(bytes < 0)
302                         {
303                                 if(errno == EINTR)
304                                         continue;
305                                 else
306                                 {
307                                         loop_running = false;
308                                         break;
309                                 }
310                         }
311                         else if(bytes >= 0)
312                         {
313                                 safe_write(size_write,&bytes,sizeof(bytes));
314
315                                 if(!check_write(stdin_write,buf,bytes))
316                                         loop_running = false;
317                                 break;
318                         }
319                 }
320         }
321
322         safe_close(stdin_write);
323         safe_close(control_read);
324
325         return NULL;
326 }
327
328 void open_console()
329 {
330         int filedes[2];
331
332         if(pipe(filedes) < 0)
333                 fatal_error("Error opening control pipe",errno);
334
335         control_read = filedes[0];
336         control_write = filedes[1];
337
338         if(pipe(filedes) < 0)
339                 fatal_error("Error opening size pipe",errno);
340
341         size_read = filedes[0];
342         size_write = filedes[1];
343
344         if(pipe(filedes) < 0)
345                 fatal_error("Error opening stdin pipe",errno);
346
347         stdin_read = filedes[0];
348         stdin_write = filedes[1];
349
350         start_thread(stdin_loop,NULL);
351 }
352
353 VM_C_API void wait_for_stdin()
354 {
355         if(write(control_write,"X",1) != 1)
356         {
357                 if(errno == EINTR)
358                         wait_for_stdin();
359                 else
360                         fatal_error("Error writing control fd",errno);
361         }
362 }
363
364 }