]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.c
Merge branch 'master' into experimental
[factor.git] / vm / os-unix.c
1 #include "master.h"
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 = 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 = 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 = 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 __attribute__((visibility("default"))) int stdin_read;
186 __attribute__((visibility("default"))) int stdin_write;
187
188 __attribute__((visibility("default"))) int control_read;
189 __attribute__((visibility("default"))) int control_write;
190
191 __attribute__((visibility("default"))) int size_read;
192 __attribute__((visibility("default"))) int size_write;
193
194 void safe_close(int fd)
195 {
196         if(close(fd) < 0)
197                 fatal_error("error closing fd",errno);
198 }
199
200 bool check_write(int fd, void *data, size_t size)
201 {
202         if(write(fd,data,size) == size)
203                 return true;
204         else
205         {
206                 if(errno == EINTR)
207                         return check_write(fd,data,size);
208                 else
209                         return false;
210         }
211 }
212
213 void safe_write(int fd, void *data, size_t size)
214 {
215         if(!check_write(fd,data,size))
216                 fatal_error("error writing fd",errno);
217 }
218
219 bool safe_read(int fd, void *data, size_t size)
220 {
221         ssize_t bytes = read(fd,data,size);
222         if(bytes < 0)
223         {
224                 if(errno == EINTR)
225                         return safe_read(fd,data,size);
226                 else
227                 {
228                         fatal_error("error reading fd",errno);
229                         return false;
230                 }
231         }
232         else
233                 return (bytes == size);
234 }
235
236 void *stdin_loop(void *arg)
237 {
238         unsigned char buf[4096];
239         bool loop_running = true;
240
241         while(loop_running)
242         {
243                 if(!safe_read(control_read,buf,1))
244                         break;
245
246                 if(buf[0] != 'X')
247                         fatal_error("stdin_loop: bad data on control fd",buf[0]);
248
249                 for(;;)
250                 {
251                         ssize_t bytes = read(0,buf,sizeof(buf));
252                         if(bytes < 0)
253                         {
254                                 if(errno == EINTR)
255                                         continue;
256                                 else
257                                 {
258                                         loop_running = false;
259                                         break;
260                                 }
261                         }
262                         else if(bytes >= 0)
263                         {
264                                 safe_write(size_write,&bytes,sizeof(bytes));
265
266                                 if(!check_write(stdin_write,buf,bytes))
267                                         loop_running = false;
268                                 break;
269                         }
270                 }
271         }
272
273         safe_close(stdin_write);
274         safe_close(control_read);
275
276         return NULL;
277 }
278
279 void open_console(void)
280 {
281         int filedes[2];
282
283         if(pipe(filedes) < 0)
284                 fatal_error("Error opening control pipe",errno);
285
286         control_read = filedes[0];
287         control_write = filedes[1];
288
289         if(pipe(filedes) < 0)
290                 fatal_error("Error opening size pipe",errno);
291
292         size_read = filedes[0];
293         size_write = filedes[1];
294
295         if(pipe(filedes) < 0)
296                 fatal_error("Error opening stdin pipe",errno);
297
298         stdin_read = filedes[0];
299         stdin_write = filedes[1];
300
301         start_thread(stdin_loop);
302 }
303
304 DLLEXPORT void wait_for_stdin(void)
305 {
306         if(write(control_write,"X",1) != 1)
307         {
308                 if(errno == EINTR)
309                         wait_for_stdin();
310                 else
311                         fatal_error("Error writing control fd",errno);
312         }
313 }