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