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