]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
vm: more commentary on lock_console() stuff
[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_raw(dll *dll, symbol_char *symbol)
51 {
52         return dlsym(dll ? dll->handle : null_dll, symbol);
53 }
54
55 void *factor_vm::ffi_dlsym(dll *dll, symbol_char *symbol)
56 {
57         return FUNCTION_CODE_POINTER(ffi_dlsym_raw(dll, symbol));
58 }
59
60 #ifdef FACTOR_PPC
61 void *factor_vm::ffi_dlsym_toc(dll *dll, symbol_char *symbol)
62 {
63         return FUNCTION_TOC_POINTER(ffi_dlsym_raw(dll, symbol));
64 }
65 #endif
66
67 void factor_vm::ffi_dlclose(dll *dll)
68 {
69         if(dlclose(dll->handle))
70                 general_error(ERROR_FFI,false_object,false_object);
71         dll->handle = NULL;
72 }
73
74 void factor_vm::primitive_existsp()
75 {
76         struct stat sb;
77         char *path = (char *)(untag_check<byte_array>(ctx->pop()) + 1);
78         ctx->push(tag_boolean(stat(path,&sb) >= 0));
79 }
80
81 void factor_vm::move_file(const vm_char *path1, const vm_char *path2)
82 {
83         int ret = 0;
84         do
85         {
86                 ret = rename((path1),(path2));
87         }
88         while(ret < 0 && errno == EINTR);
89
90         if(ret < 0)
91                 general_error(ERROR_IO,tag_fixnum(errno),false_object);
92 }
93
94 segment::segment(cell size_, bool executable_p)
95 {
96         size = size_;
97
98         int pagesize = getpagesize();
99
100         int prot;
101         if(executable_p)
102                 prot = (PROT_READ | PROT_WRITE | PROT_EXEC);
103         else
104                 prot = (PROT_READ | PROT_WRITE);
105
106         char *array = (char *)mmap(NULL,pagesize + size + pagesize,prot,MAP_ANON | MAP_PRIVATE,-1,0);
107         if(array == (char*)-1) out_of_memory();
108
109         if(mprotect(array,pagesize,PROT_NONE) == -1)
110                 fatal_error("Cannot protect low guard page",(cell)array);
111
112         if(mprotect(array + pagesize + size,pagesize,PROT_NONE) == -1)
113                 fatal_error("Cannot protect high guard page",(cell)array);
114
115         start = (cell)(array + pagesize);
116         end = start + size;
117 }
118
119 segment::~segment()
120 {
121         int pagesize = getpagesize();
122         int retval = munmap((void*)(start - pagesize),pagesize + size + pagesize);
123         if(retval)
124                 fatal_error("Segment deallocation failed",0);
125 }
126
127 void code_heap::guard_safepoint()
128 {
129         if(mprotect(safepoint_page,getpagesize(),PROT_NONE) == -1)
130                 fatal_error("Cannot protect safepoint guard page",(cell)safepoint_page);
131 }
132
133 void code_heap::unguard_safepoint()
134 {
135         if(mprotect(safepoint_page,getpagesize(),PROT_WRITE) == -1)
136                 fatal_error("Cannot unprotect safepoint guard page",(cell)safepoint_page);
137 }
138
139 void factor_vm::dispatch_signal(void *uap, void (handler)())
140 {
141         dispatch_signal_handler(
142                 (cell*)&UAP_STACK_POINTER(uap),
143                 (cell*)&UAP_PROGRAM_COUNTER(uap),
144                 (cell)FUNCTION_CODE_POINTER(handler)
145         );
146         UAP_SET_TOC_POINTER(uap, (cell)FUNCTION_TOC_POINTER(handler));
147 }
148
149 void factor_vm::start_sampling_profiler_timer()
150 {
151         struct itimerval timer;
152         memset((void*)&timer, 0, sizeof(struct itimerval));
153         timer.it_value.tv_usec = 1000000/samples_per_second;
154         timer.it_interval.tv_usec = 1000000/samples_per_second;
155         setitimer(ITIMER_REAL, &timer, NULL);
156 }
157
158 void factor_vm::end_sampling_profiler_timer()
159 {
160         struct itimerval timer;
161         memset((void*)&timer, 0, sizeof(struct itimerval));
162         setitimer(ITIMER_REAL, &timer, NULL);
163 }
164
165 void memory_signal_handler(int signal, siginfo_t *siginfo, void *uap)
166 {
167         factor_vm *vm = current_vm();
168         vm->signal_fault_addr = (cell)siginfo->si_addr;
169         vm->dispatch_signal(uap,factor::memory_signal_handler_impl);
170 }
171
172 void synchronous_signal_handler(int signal, siginfo_t *siginfo, void *uap)
173 {
174         factor_vm *vm = current_vm_p();
175         if (vm)
176         {
177                 vm->signal_number = signal;
178                 vm->dispatch_signal(uap,factor::synchronous_signal_handler_impl);
179         } else
180                 fatal_error("Foreign thread received signal", signal);
181 }
182
183 void safe_write(int fd, void *data, ssize_t size);
184
185 static void enqueue_signal(factor_vm *vm, int signal)
186 {
187         if (vm->signal_pipe_output != 0)
188                 safe_write(vm->signal_pipe_output, &signal, sizeof(int));
189 }
190
191 void enqueue_signal_handler(int signal, siginfo_t *siginfo, void *uap)
192 {
193         factor_vm *vm = current_vm_p();
194         if (vm)
195                 enqueue_signal(vm, signal);
196         else
197                 fatal_error("Foreign thread received signal", signal);
198 }
199
200 void fep_signal_handler(int signal, siginfo_t *siginfo, void *uap)
201 {
202         factor_vm *vm = current_vm_p();
203         if (vm)
204         {
205                 vm->safepoint.enqueue_fep();
206                 enqueue_signal(vm, signal);
207         }
208         else
209                 fatal_error("Foreign thread received signal", signal);
210 }
211
212 void sample_signal_handler(int signal, siginfo_t *siginfo, void *uap)
213 {
214         factor_vm *vm = current_vm_p();
215         if (vm)
216                 vm->safepoint.enqueue_samples(1, (cell)UAP_PROGRAM_COUNTER(uap), false);
217         else if (thread_vms.size() == 1) {
218                 factor_vm *the_only_vm = thread_vms.begin()->second;
219                 the_only_vm->safepoint.enqueue_samples(1, (cell)UAP_PROGRAM_COUNTER(uap), true);
220         }
221 }
222
223 void ignore_signal_handler(int signal, siginfo_t *siginfo, void *uap)
224 {
225 }
226
227 void fpe_signal_handler(int signal, siginfo_t *siginfo, void *uap)
228 {
229         factor_vm *vm = current_vm();
230         vm->signal_number = signal;
231         vm->signal_fpu_status = fpu_status(uap_fpu_status(uap));
232         uap_clear_fpu_status(uap);
233
234         vm->dispatch_signal(uap,
235                 (siginfo->si_code == FPE_INTDIV || siginfo->si_code == FPE_INTOVF)
236                 ? factor::synchronous_signal_handler_impl
237                 : factor::fp_signal_handler_impl);
238 }
239
240 static void sigaction_safe(int signum, const struct sigaction *act, struct sigaction *oldact)
241 {
242         int ret;
243         do
244         {
245                 ret = sigaction(signum, act, oldact);
246         }
247         while(ret == -1 && errno == EINTR);
248
249         if(ret == -1)
250                 fatal_error("sigaction failed", 0);
251 }
252
253 static void init_sigaction_with_handler(struct sigaction *act,
254         void (*handler)(int, siginfo_t*, void*))
255 {
256         memset(act, 0, sizeof(struct sigaction));
257         sigemptyset(&act->sa_mask);
258         act->sa_sigaction = handler;
259         act->sa_flags = SA_SIGINFO | SA_ONSTACK;
260 }
261
262 static void safe_pipe(int *in, int *out)
263 {
264         int filedes[2];
265
266         if(pipe(filedes) < 0)
267                 fatal_error("Error opening pipe",errno);
268
269         *in = filedes[0];
270         *out = filedes[1];
271
272         if(fcntl(*in,F_SETFD,FD_CLOEXEC) < 0)
273                 fatal_error("Error with fcntl",errno);
274
275         if(fcntl(*out,F_SETFD,FD_CLOEXEC) < 0)
276                 fatal_error("Error with fcntl",errno);
277 }
278
279 static void init_signal_pipe(factor_vm *vm)
280 {
281         safe_pipe(&vm->signal_pipe_input, &vm->signal_pipe_output);
282         vm->special_objects[OBJ_SIGNAL_PIPE] = tag_fixnum(vm->signal_pipe_input);
283 }
284
285 void factor_vm::unix_init_signals()
286 {
287         init_signal_pipe(this);
288
289         signal_callstack_seg = new segment(callstack_size,false);
290
291         stack_t signal_callstack;
292         signal_callstack.ss_sp = (char *)signal_callstack_seg->start;
293         signal_callstack.ss_size = signal_callstack_seg->size;
294         signal_callstack.ss_flags = 0;
295
296         if(sigaltstack(&signal_callstack,(stack_t *)NULL) < 0)
297                 fatal_error("sigaltstack() failed",0);
298
299         struct sigaction memory_sigaction;
300         struct sigaction synchronous_sigaction;
301         struct sigaction enqueue_sigaction;
302         struct sigaction fep_sigaction;
303         struct sigaction sample_sigaction;
304         struct sigaction fpe_sigaction;
305         struct sigaction ignore_sigaction;
306
307         init_sigaction_with_handler(&memory_sigaction, memory_signal_handler);
308         sigaction_safe(SIGBUS,&memory_sigaction,NULL);
309         sigaction_safe(SIGSEGV,&memory_sigaction,NULL);
310         sigaction_safe(SIGTRAP,&memory_sigaction,NULL);
311
312         init_sigaction_with_handler(&fpe_sigaction, fpe_signal_handler);
313         sigaction_safe(SIGFPE,&fpe_sigaction,NULL);
314
315         init_sigaction_with_handler(&synchronous_sigaction, synchronous_signal_handler);
316         sigaction_safe(SIGILL,&synchronous_sigaction,NULL);
317         sigaction_safe(SIGABRT,&synchronous_sigaction,NULL);
318
319         init_sigaction_with_handler(&enqueue_sigaction, enqueue_signal_handler);
320         sigaction_safe(SIGWINCH,&enqueue_sigaction,NULL);
321 #ifdef SIGINFO
322         sigaction_safe(SIGINFO,&enqueue_sigaction,NULL);
323 #endif
324
325         init_sigaction_with_handler(&fep_sigaction, fep_signal_handler);
326         sigaction_safe(SIGQUIT,&fep_sigaction,NULL);
327         sigaction_safe(SIGINT,&fep_sigaction,NULL);
328
329         init_sigaction_with_handler(&sample_sigaction, sample_signal_handler);
330         sigaction_safe(SIGALRM,&sample_sigaction,NULL);
331
332         /* We don't use SA_IGN here because then the ignore action is inherited
333         by subprocesses, which we don't want. There is a unit test in
334         io.launcher.unix for this. */
335         init_sigaction_with_handler(&ignore_sigaction, ignore_signal_handler);
336         sigaction_safe(SIGPIPE,&ignore_sigaction,NULL);
337         /* We send SIGUSR2 to the stdin_loop thread to interrupt it on FEP */
338         sigaction_safe(SIGUSR2,&ignore_sigaction,NULL);
339 }
340
341 /* On Unix, shared fds such as stdin cannot be set to non-blocking mode
342 (http://homepages.tesco.net/J.deBoynePollard/FGA/dont-set-shared-file-descriptors-to-non-blocking-mode.html)
343 so we kludge around this by spawning a thread, which waits on a control pipe
344 for a signal, upon receiving this signal it reads one block of data from stdin
345 and writes it to a data pipe. Upon completion, it writes a 4-byte integer to
346 the size pipe, indicating how much data was written to the data pipe.
347
348 The read end of the size pipe can be set to non-blocking. */
349 extern "C" {
350         int stdin_read;
351         int stdin_write;
352
353         int control_read;
354         int control_write;
355
356         int size_read;
357         int size_write;
358
359         THREADHANDLE stdin_thread;
360         pthread_mutex_t stdin_mutex;
361 }
362
363 void safe_close(int fd)
364 {
365         if(close(fd) < 0)
366                 fatal_error("error closing fd",errno);
367 }
368
369 bool check_write(int fd, void *data, ssize_t size)
370 {
371         if(write(fd,data,size) == size)
372                 return true;
373         else
374         {
375                 if(errno == EINTR)
376                         return check_write(fd,data,size);
377                 else
378                         return false;
379         }
380 }
381
382 void safe_write(int fd, void *data, ssize_t size)
383 {
384         if(!check_write(fd,data,size))
385                 fatal_error("error writing fd",errno);
386 }
387
388 bool safe_read(int fd, void *data, ssize_t size)
389 {
390         ssize_t bytes = read(fd,data,size);
391         if(bytes < 0)
392         {
393                 if(errno == EINTR)
394                         return safe_read(fd,data,size);
395                 else
396                 {
397                         fatal_error("error reading fd",errno);
398                         return false;
399                 }
400         }
401         else
402                 return (bytes == size);
403 }
404
405 void *stdin_loop(void *arg)
406 {
407         unsigned char buf[4096];
408         bool loop_running = true;
409
410         sigset_t mask;
411         sigfillset(&mask);
412         sigdelset(&mask, SIGUSR2);
413         sigdelset(&mask, SIGTTIN);
414         pthread_sigmask(SIG_SETMASK, &mask, NULL);
415
416         while(loop_running)
417         {
418                 if(!safe_read(control_read,buf,1))
419                         break;
420
421                 if(buf[0] != 'X')
422                         fatal_error("stdin_loop: bad data on control fd",buf[0]);
423
424                 for(;;)
425                 {
426                         // If we fep, the parent thread will grab stdin_mutex and send us
427                         // SIGUSR2 to interrupt the read() call.
428                         pthread_mutex_lock(&stdin_mutex);
429                         pthread_mutex_unlock(&stdin_mutex);
430                         ssize_t bytes = read(0,buf,sizeof(buf));
431                         if(bytes < 0)
432                         {
433                                 if(errno == EINTR)
434                                         continue;
435                                 else
436                                 {
437                                         loop_running = false;
438                                         break;
439                                 }
440                         }
441                         else if(bytes >= 0)
442                         {
443                                 safe_write(size_write,&bytes,sizeof(bytes));
444
445                                 if(!check_write(stdin_write,buf,bytes))
446                                         loop_running = false;
447                                 break;
448                         }
449                 }
450         }
451
452         safe_close(stdin_write);
453         safe_close(control_read);
454
455         return NULL;
456 }
457
458 void factor_vm::open_console()
459 {
460         safe_pipe(&control_read,&control_write);
461         safe_pipe(&size_read,&size_write);
462         safe_pipe(&stdin_read,&stdin_write);
463         stdin_thread = start_thread(stdin_loop,NULL);
464         pthread_mutex_init(&stdin_mutex, NULL);
465 }
466
467 void factor_vm::lock_console()
468 {
469         // Lock the stdin_mutex and send the stdin_loop thread a signal to interrupt
470         // any read() it has in progress. When the stdin loop iterates again, it will
471         // try to lock the same mutex and wait until unlock_console() is called.
472         pthread_mutex_lock(&stdin_mutex);
473         pthread_kill(stdin_thread, SIGUSR2);
474 }
475
476 void factor_vm::unlock_console()
477 {
478         pthread_mutex_unlock(&stdin_mutex);
479 }
480
481 }