]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
misc small documentation fixes, some fixes for factor.vim, changed permissions of...
[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), 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 *)(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 *)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(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 static void sigaction_safe(int signum, const struct sigaction *act, struct sigaction *oldact)
136 {
137         int ret;
138         do
139         {
140                 ret = sigaction(signum, act, oldact);
141         }
142         while(ret == -1 && errno == EINTR);
143
144         if(ret == -1)
145                 fatal_error("sigaction failed", 0);
146 }
147
148 void unix_init_signals()
149 {
150         struct sigaction memory_sigaction;
151         struct sigaction misc_sigaction;
152         struct sigaction ignore_sigaction;
153
154         memset(&memory_sigaction,0,sizeof(struct sigaction));
155         sigemptyset(&memory_sigaction.sa_mask);
156         memory_sigaction.sa_sigaction = memory_signal_handler;
157         memory_sigaction.sa_flags = SA_SIGINFO;
158
159         sigaction_safe(SIGBUS,&memory_sigaction,NULL);
160         sigaction_safe(SIGSEGV,&memory_sigaction,NULL);
161
162         memset(&misc_sigaction,0,sizeof(struct sigaction));
163         sigemptyset(&misc_sigaction.sa_mask);
164         misc_sigaction.sa_sigaction = misc_signal_handler;
165         misc_sigaction.sa_flags = SA_SIGINFO;
166
167         sigaction_safe(SIGABRT,&misc_sigaction,NULL);
168         sigaction_safe(SIGFPE,&misc_sigaction,NULL);
169         sigaction_safe(SIGQUIT,&misc_sigaction,NULL);
170         sigaction_safe(SIGILL,&misc_sigaction,NULL);
171
172         memset(&ignore_sigaction,0,sizeof(struct sigaction));
173         sigemptyset(&ignore_sigaction.sa_mask);
174         ignore_sigaction.sa_handler = SIG_IGN;
175         sigaction_safe(SIGPIPE,&ignore_sigaction,NULL);
176 }
177
178 /* On Unix, shared fds such as stdin cannot be set to non-blocking mode
179 (http://homepages.tesco.net/J.deBoynePollard/FGA/dont-set-shared-file-descriptors-to-non-blocking-mode.html)
180 so we kludge around this by spawning a thread, which waits on a control pipe
181 for a signal, upon receiving this signal it reads one block of data from stdin
182 and writes it to a data pipe. Upon completion, it writes a 4-byte integer to
183 the size pipe, indicating how much data was written to the data pipe.
184
185 The read end of the size pipe can be set to non-blocking. */
186 extern "C" {
187         int stdin_read;
188         int stdin_write;
189
190         int control_read;
191         int control_write;
192
193         int size_read;
194         int size_write;
195 }
196
197 void safe_close(int fd)
198 {
199         if(close(fd) < 0)
200                 fatal_error("error closing fd",errno);
201 }
202
203 bool check_write(int fd, void *data, ssize_t size)
204 {
205         if(write(fd,data,size) == size)
206                 return true;
207         else
208         {
209                 if(errno == EINTR)
210                         return check_write(fd,data,size);
211                 else
212                         return false;
213         }
214 }
215
216 void safe_write(int fd, void *data, ssize_t size)
217 {
218         if(!check_write(fd,data,size))
219                 fatal_error("error writing fd",errno);
220 }
221
222 bool safe_read(int fd, void *data, ssize_t size)
223 {
224         ssize_t bytes = read(fd,data,size);
225         if(bytes < 0)
226         {
227                 if(errno == EINTR)
228                         return safe_read(fd,data,size);
229                 else
230                 {
231                         fatal_error("error reading fd",errno);
232                         return false;
233                 }
234         }
235         else
236                 return (bytes == size);
237 }
238
239 void *stdin_loop(void *arg)
240 {
241         unsigned char buf[4096];
242         bool loop_running = true;
243
244         while(loop_running)
245         {
246                 if(!safe_read(control_read,buf,1))
247                         break;
248
249                 if(buf[0] != 'X')
250                         fatal_error("stdin_loop: bad data on control fd",buf[0]);
251
252                 for(;;)
253                 {
254                         ssize_t bytes = read(0,buf,sizeof(buf));
255                         if(bytes < 0)
256                         {
257                                 if(errno == EINTR)
258                                         continue;
259                                 else
260                                 {
261                                         loop_running = false;
262                                         break;
263                                 }
264                         }
265                         else if(bytes >= 0)
266                         {
267                                 safe_write(size_write,&bytes,sizeof(bytes));
268
269                                 if(!check_write(stdin_write,buf,bytes))
270                                         loop_running = false;
271                                 break;
272                         }
273                 }
274         }
275
276         safe_close(stdin_write);
277         safe_close(control_read);
278
279         return NULL;
280 }
281
282 void open_console()
283 {
284         int filedes[2];
285
286         if(pipe(filedes) < 0)
287                 fatal_error("Error opening control pipe",errno);
288
289         control_read = filedes[0];
290         control_write = filedes[1];
291
292         if(pipe(filedes) < 0)
293                 fatal_error("Error opening size pipe",errno);
294
295         size_read = filedes[0];
296         size_write = filedes[1];
297
298         if(pipe(filedes) < 0)
299                 fatal_error("Error opening stdin pipe",errno);
300
301         stdin_read = filedes[0];
302         stdin_write = filedes[1];
303
304         start_thread(stdin_loop);
305 }
306
307 VM_C_API void wait_for_stdin()
308 {
309         if(write(control_write,"X",1) != 1)
310         {
311                 if(errno == EINTR)
312                         wait_for_stdin();
313                 else
314                         fatal_error("Error writing control fd",errno);
315         }
316 }
317
318 }