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