]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.c
Merge branch 'master' into experimental (untested!)
[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 void 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                         safe_read(fd,data,size);
226                 else
227                         fatal_error("error reading fd",errno);
228         }
229         else if(bytes != size)
230                 fatal_error("unexpected eof on fd",bytes);
231 }
232
233 void *stdin_loop(void *arg)
234 {
235         unsigned char buf[4096];
236         bool loop_running = true;
237
238         while(loop_running)
239         {
240                 safe_read(control_read,buf,1);
241                 if(buf[0] != 'X')
242                         fatal_error("stdin_loop: bad data on control fd",buf[0]);
243
244                 for(;;)
245                 {
246                         ssize_t bytes = read(0,buf,sizeof(buf));
247                         if(bytes < 0)
248                         {
249                                 if(errno == EINTR)
250                                         continue;
251                                 else
252                                 {
253                                         loop_running = false;
254                                         break;
255                                 }
256                         }
257                         else if(bytes >= 0)
258                         {
259                                 safe_write(size_write,&bytes,sizeof(bytes));
260
261                                 if(write(stdin_write,buf,bytes) != bytes)
262                                         loop_running = false;
263                                 break;
264                         }
265                 }
266         }
267
268
269         safe_close(stdin_write);
270         safe_close(control_write);
271
272         return NULL;
273 }
274
275 void open_console(void)
276 {
277         int filedes[2];
278
279         if(pipe(filedes) < 0)
280                 fatal_error("Error opening control pipe",errno);
281
282         control_read = filedes[0];
283         control_write = filedes[1];
284
285         if(pipe(filedes) < 0)
286                 fatal_error("Error opening size pipe",errno);
287
288         size_read = filedes[0];
289         size_write = filedes[1];
290
291         if(pipe(filedes) < 0)
292                 fatal_error("Error opening stdin pipe",errno);
293
294         stdin_read = filedes[0];
295         stdin_write = filedes[1];
296
297         start_thread(stdin_loop);
298 }
299
300 DLLEXPORT void wait_for_stdin(void)
301 {
302         if(write(control_write,"X",1) != 1)
303         {
304                 if(errno == EINTR)
305                         wait_for_stdin();
306                 else
307                         fatal_error("Error writing control fd",errno);
308         }
309 }