]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
Merge branch 'docs-linearization-typo' of http://github.com/mncharity/factor
[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_JOINABLE) != 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 static void *null_dll;
21
22 void sleep_nanos(u64 nsec)
23 {
24         timespec ts;
25         timespec ts_rem;
26         int ret;
27         ts.tv_sec = nsec / 1000000000;
28         ts.tv_nsec = nsec % 1000000000;
29         ret = nanosleep(&ts,&ts_rem);
30         while(ret == -1 && errno == EINTR)
31         {
32                 memcpy(&ts, &ts_rem, sizeof(ts));
33                 ret = nanosleep(&ts, &ts_rem);
34         }
35
36         if(ret == -1)
37                 fatal_error("nanosleep failed", 0);
38 }
39
40 void factor_vm::init_ffi()
41 {
42         null_dll = dlopen(NULL,RTLD_LAZY);
43 }
44
45 void factor_vm::ffi_dlopen(dll *dll)
46 {
47         dll->handle = dlopen(alien_offset(dll->path), RTLD_LAZY);
48 }
49
50 void *factor_vm::ffi_dlsym(dll *dll, symbol_char *symbol)
51 {
52         void *handle = (dll == NULL ? null_dll : dll->handle);
53         return dlsym(handle,symbol);
54 }
55
56 void factor_vm::ffi_dlclose(dll *dll)
57 {
58         if(dlclose(dll->handle))
59                 general_error(ERROR_FFI,false_object,false_object);
60         dll->handle = NULL;
61 }
62
63 void factor_vm::primitive_existsp()
64 {
65         struct stat sb;
66         char *path = (char *)(untag_check<byte_array>(ctx->pop()) + 1);
67         ctx->push(tag_boolean(stat(path,&sb) >= 0));
68 }
69
70 void factor_vm::move_file(const vm_char *path1, const vm_char *path2)
71 {
72         int ret = 0;
73         do
74         {
75                 ret = rename((path1),(path2));
76         }
77         while(ret < 0 && errno == EINTR);
78
79         if(ret < 0)
80                 general_error(ERROR_IO,tag_fixnum(errno),false_object);
81 }
82
83 segment::segment(cell size_, bool executable_p)
84 {
85         size = size_;
86
87         int pagesize = getpagesize();
88
89         int prot;
90         if(executable_p)
91                 prot = (PROT_READ | PROT_WRITE | PROT_EXEC);
92         else
93                 prot = (PROT_READ | PROT_WRITE);
94
95         char *array = (char *)mmap(NULL,pagesize + size + pagesize,prot,MAP_ANON | MAP_PRIVATE,-1,0);
96         if(array == (char*)-1) out_of_memory();
97
98         if(mprotect(array,pagesize,PROT_NONE) == -1)
99                 fatal_error("Cannot protect low guard page",(cell)array);
100
101         if(mprotect(array + pagesize + size,pagesize,PROT_NONE) == -1)
102                 fatal_error("Cannot protect high guard page",(cell)array);
103
104         start = (cell)(array + pagesize);
105         end = start + size;
106 }
107
108 segment::~segment()
109 {
110         int pagesize = getpagesize();
111         int retval = munmap((void*)(start - pagesize),pagesize + size + pagesize);
112         if(retval)
113                 fatal_error("Segment deallocation failed",0);
114 }
115
116 void factor_vm::dispatch_signal(void *uap, void (handler)())
117 {
118         UAP_STACK_POINTER(uap) = (UAP_STACK_POINTER_TYPE)fix_callstack_top((stack_frame *)UAP_STACK_POINTER(uap));
119         UAP_PROGRAM_COUNTER(uap) = (cell)handler;
120
121         ctx->callstack_top = (stack_frame *)UAP_STACK_POINTER(uap);
122 }
123
124 void memory_signal_handler(int signal, siginfo_t *siginfo, void *uap)
125 {
126         factor_vm *vm = current_vm();
127         vm->signal_fault_addr = (cell)siginfo->si_addr;
128         vm->dispatch_signal(uap,factor::memory_signal_handler_impl);
129 }
130
131 void misc_signal_handler(int signal, siginfo_t *siginfo, void *uap)
132 {
133         factor_vm *vm = current_vm();
134         vm->signal_number = signal;
135         vm->dispatch_signal(uap,factor::misc_signal_handler_impl);
136 }
137
138 void ignore_signal_handler(int signal, siginfo_t *siginfo, void *uap)
139 {
140 }
141
142 void fpe_signal_handler(int signal, siginfo_t *siginfo, void *uap)
143 {
144         factor_vm *vm = current_vm();
145         vm->signal_number = signal;
146         vm->signal_fpu_status = fpu_status(uap_fpu_status(uap));
147         uap_clear_fpu_status(uap);
148
149         vm->dispatch_signal(uap,
150                 (siginfo->si_code == FPE_INTDIV || siginfo->si_code == FPE_INTOVF)
151                 ? factor::misc_signal_handler_impl
152                 : factor::fp_signal_handler_impl);
153 }
154
155 static void sigaction_safe(int signum, const struct sigaction *act, struct sigaction *oldact)
156 {
157         int ret;
158         do
159         {
160                 ret = sigaction(signum, act, oldact);
161         }
162         while(ret == -1 && errno == EINTR);
163
164         if(ret == -1)
165                 fatal_error("sigaction failed", 0);
166 }
167
168 void factor_vm::unix_init_signals()
169 {
170         /* OpenBSD doesn't support sigaltstack() if we link against
171         libpthread. See http://redmine.ruby-lang.org/issues/show/1239 */
172
173 #ifndef __OpenBSD__
174         signal_callstack_seg = new segment(callstack_size,false);
175
176         stack_t signal_callstack;
177         signal_callstack.ss_sp = (char *)signal_callstack_seg->start;
178         signal_callstack.ss_size = signal_callstack_seg->size;
179         signal_callstack.ss_flags = 0;
180
181         if(sigaltstack(&signal_callstack,(stack_t *)NULL) < 0)
182                 fatal_error("sigaltstack() failed",0);
183 #endif
184
185         struct sigaction memory_sigaction;
186         struct sigaction misc_sigaction;
187         struct sigaction fpe_sigaction;
188         struct sigaction ignore_sigaction;
189
190         memset(&memory_sigaction,0,sizeof(struct sigaction));
191         sigemptyset(&memory_sigaction.sa_mask);
192         memory_sigaction.sa_sigaction = memory_signal_handler;
193         memory_sigaction.sa_flags = SA_SIGINFO | SA_ONSTACK;
194
195         sigaction_safe(SIGBUS,&memory_sigaction,NULL);
196         sigaction_safe(SIGSEGV,&memory_sigaction,NULL);
197
198         memset(&fpe_sigaction,0,sizeof(struct sigaction));
199         sigemptyset(&fpe_sigaction.sa_mask);
200         fpe_sigaction.sa_sigaction = fpe_signal_handler;
201         fpe_sigaction.sa_flags = SA_SIGINFO | SA_ONSTACK;
202
203         sigaction_safe(SIGFPE,&fpe_sigaction,NULL);
204
205         memset(&misc_sigaction,0,sizeof(struct sigaction));
206         sigemptyset(&misc_sigaction.sa_mask);
207         misc_sigaction.sa_sigaction = misc_signal_handler;
208         misc_sigaction.sa_flags = SA_SIGINFO | SA_ONSTACK;
209
210         sigaction_safe(SIGQUIT,&misc_sigaction,NULL);
211         sigaction_safe(SIGILL,&misc_sigaction,NULL);
212
213         /* We don't use SA_IGN here because then the ignore action is inherited
214         by subprocesses, which we don't want. There is a unit test in
215         io.launcher.unix for this. */
216         memset(&ignore_sigaction,0,sizeof(struct sigaction));
217         sigemptyset(&ignore_sigaction.sa_mask);
218         ignore_sigaction.sa_sigaction = ignore_signal_handler;
219         ignore_sigaction.sa_flags = SA_SIGINFO | SA_ONSTACK;
220         sigaction_safe(SIGPIPE,&ignore_sigaction,NULL);
221 }
222
223 /* On Unix, shared fds such as stdin cannot be set to non-blocking mode
224 (http://homepages.tesco.net/J.deBoynePollard/FGA/dont-set-shared-file-descriptors-to-non-blocking-mode.html)
225 so we kludge around this by spawning a thread, which waits on a control pipe
226 for a signal, upon receiving this signal it reads one block of data from stdin
227 and writes it to a data pipe. Upon completion, it writes a 4-byte integer to
228 the size pipe, indicating how much data was written to the data pipe.
229
230 The read end of the size pipe can be set to non-blocking. */
231 extern "C" {
232         int stdin_read;
233         int stdin_write;
234
235         int control_read;
236         int control_write;
237
238         int size_read;
239         int size_write;
240 }
241
242 void safe_close(int fd)
243 {
244         if(close(fd) < 0)
245                 fatal_error("error closing fd",errno);
246 }
247
248 bool check_write(int fd, void *data, ssize_t size)
249 {
250         if(write(fd,data,size) == size)
251                 return true;
252         else
253         {
254                 if(errno == EINTR)
255                         return check_write(fd,data,size);
256                 else
257                         return false;
258         }
259 }
260
261 void safe_write(int fd, void *data, ssize_t size)
262 {
263         if(!check_write(fd,data,size))
264                 fatal_error("error writing fd",errno);
265 }
266
267 bool safe_read(int fd, void *data, ssize_t size)
268 {
269         ssize_t bytes = read(fd,data,size);
270         if(bytes < 0)
271         {
272                 if(errno == EINTR)
273                         return safe_read(fd,data,size);
274                 else
275                 {
276                         fatal_error("error reading fd",errno);
277                         return false;
278                 }
279         }
280         else
281                 return (bytes == size);
282 }
283
284 void *stdin_loop(void *arg)
285 {
286         unsigned char buf[4096];
287         bool loop_running = true;
288
289         while(loop_running)
290         {
291                 if(!safe_read(control_read,buf,1))
292                         break;
293
294                 if(buf[0] != 'X')
295                         fatal_error("stdin_loop: bad data on control fd",buf[0]);
296
297                 for(;;)
298                 {
299                         ssize_t bytes = read(0,buf,sizeof(buf));
300                         if(bytes < 0)
301                         {
302                                 if(errno == EINTR)
303                                         continue;
304                                 else
305                                 {
306                                         loop_running = false;
307                                         break;
308                                 }
309                         }
310                         else if(bytes >= 0)
311                         {
312                                 safe_write(size_write,&bytes,sizeof(bytes));
313
314                                 if(!check_write(stdin_write,buf,bytes))
315                                         loop_running = false;
316                                 break;
317                         }
318                 }
319         }
320
321         safe_close(stdin_write);
322         safe_close(control_read);
323
324         return NULL;
325 }
326
327 void open_console()
328 {
329         int filedes[2];
330
331         if(pipe(filedes) < 0)
332                 fatal_error("Error opening control pipe",errno);
333
334         control_read = filedes[0];
335         control_write = filedes[1];
336
337         if(pipe(filedes) < 0)
338                 fatal_error("Error opening size pipe",errno);
339
340         size_read = filedes[0];
341         size_write = filedes[1];
342
343         if(pipe(filedes) < 0)
344                 fatal_error("Error opening stdin pipe",errno);
345
346         stdin_read = filedes[0];
347         stdin_write = filedes[1];
348
349         start_thread(stdin_loop,NULL);
350 }
351
352 VM_C_API void wait_for_stdin()
353 {
354         if(write(control_write,"X",1) != 1)
355         {
356                 if(errno == EINTR)
357                         wait_for_stdin();
358                 else
359                         fatal_error("Error writing control fd",errno);
360         }
361 }
362
363 }