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