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