]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
vm: split up TLS code and add a dummy implementation for a dummy OS known as NetBSD
[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 u64 system_micros()
23 {
24         struct timeval t;
25         gettimeofday(&t,NULL);
26         return (u64)t.tv_sec * 1000000 + t.tv_usec;
27 }
28
29 void sleep_nanos(u64 nsec)
30 {
31         timespec ts;
32         timespec ts_rem;
33         int ret;
34         ts.tv_sec = nsec / 1000000000;
35         ts.tv_nsec = nsec % 1000000000;
36         ret = nanosleep(&ts,&ts_rem);
37         while(ret == -1 && errno == EINTR)
38         {
39                 memcpy(&ts, &ts_rem, sizeof(ts));
40                 ret = nanosleep(&ts, &ts_rem);
41         }
42
43         if(ret == -1)
44                 fatal_error("nanosleep failed", 0);
45 }
46
47 void factor_vm::init_ffi()
48 {
49         /* NULL_DLL is "libfactor.dylib" for OS X and NULL for generic Unix */
50         null_dll = dlopen(NULL_DLL,RTLD_LAZY);
51 }
52
53 void factor_vm::ffi_dlopen(dll *dll)
54 {
55         dll->handle = dlopen(alien_offset(dll->path), RTLD_LAZY);
56 }
57
58 void *factor_vm::ffi_dlsym(dll *dll, symbol_char *symbol)
59 {
60         void *handle = (dll == NULL ? null_dll : dll->handle);
61         return dlsym(handle,symbol);
62 }
63
64 void factor_vm::ffi_dlclose(dll *dll)
65 {
66         if(dlclose(dll->handle))
67                 general_error(ERROR_FFI,false_object,false_object);
68         dll->handle = NULL;
69 }
70
71 void factor_vm::primitive_existsp()
72 {
73         struct stat sb;
74         char *path = (char *)(untag_check<byte_array>(ctx->pop()) + 1);
75         ctx->push(tag_boolean(stat(path,&sb) >= 0));
76 }
77
78 void factor_vm::move_file(const vm_char *path1, const vm_char *path2)
79 {
80         int ret = 0;
81         do
82         {
83                 ret = rename((path1),(path2));
84         }
85         while(ret < 0 && errno == EINTR);
86
87         if(ret < 0)
88                 general_error(ERROR_IO,tag_fixnum(errno),false_object);
89 }
90
91 segment::segment(cell size_, bool executable_p)
92 {
93         size = size_;
94
95         int pagesize = getpagesize();
96
97         int prot;
98         if(executable_p)
99                 prot = (PROT_READ | PROT_WRITE | PROT_EXEC);
100         else
101                 prot = (PROT_READ | PROT_WRITE);
102
103         char *array = (char *)mmap(NULL,pagesize + size + pagesize,prot,MAP_ANON | MAP_PRIVATE,-1,0);
104         if(array == (char*)-1) out_of_memory();
105
106         if(mprotect(array,pagesize,PROT_NONE) == -1)
107                 fatal_error("Cannot protect low guard page",(cell)array);
108
109         if(mprotect(array + pagesize + size,pagesize,PROT_NONE) == -1)
110                 fatal_error("Cannot protect high guard page",(cell)array);
111
112         start = (cell)(array + pagesize);
113         end = start + size;
114 }
115
116 segment::~segment()
117 {
118         int pagesize = getpagesize();
119         int retval = munmap((void*)(start - pagesize),pagesize + size + pagesize);
120         if(retval)
121                 fatal_error("Segment deallocation failed",0);
122 }
123
124 void factor_vm::dispatch_signal(void *uap, void (handler)())
125 {
126         UAP_STACK_POINTER(uap) = (UAP_STACK_POINTER_TYPE)fix_callstack_top((stack_frame *)UAP_STACK_POINTER(uap));
127         UAP_PROGRAM_COUNTER(uap) = (cell)handler;
128
129         signal_callstack_top = (stack_frame *)UAP_STACK_POINTER(uap);
130 }
131
132 void memory_signal_handler(int signal, siginfo_t *siginfo, void *uap)
133 {
134         factor_vm *vm = current_vm();
135         vm->signal_fault_addr = (cell)siginfo->si_addr;
136         vm->dispatch_signal(uap,factor::memory_signal_handler_impl);
137 }
138
139 void misc_signal_handler(int signal, siginfo_t *siginfo, void *uap)
140 {
141         factor_vm *vm = current_vm();
142         vm->signal_number = signal;
143         vm->dispatch_signal(uap,factor::misc_signal_handler_impl);
144 }
145
146 void fpe_signal_handler(int signal, siginfo_t *siginfo, void *uap)
147 {
148         factor_vm *vm = current_vm();
149         vm->signal_number = signal;
150         vm->signal_fpu_status = fpu_status(uap_fpu_status(uap));
151         uap_clear_fpu_status(uap);
152
153         vm->dispatch_signal(uap,
154                 (siginfo->si_code == FPE_INTDIV || siginfo->si_code == FPE_INTOVF)
155                 ? factor::misc_signal_handler_impl
156                 : factor::fp_signal_handler_impl);
157 }
158
159 static void sigaction_safe(int signum, const struct sigaction *act, struct sigaction *oldact)
160 {
161         int ret;
162         do
163         {
164                 ret = sigaction(signum, act, oldact);
165         }
166         while(ret == -1 && errno == EINTR);
167
168         if(ret == -1)
169                 fatal_error("sigaction failed", 0);
170 }
171
172 void factor_vm::unix_init_signals()
173 {
174         /* OpenBSD doesn't support sigaltstack() if we link against
175         libpthread. See http://redmine.ruby-lang.org/issues/show/1239 */
176
177 #ifndef __OpenBSD__
178         signal_callstack_seg = new segment(callstack_size,false);
179
180         stack_t signal_callstack;
181         signal_callstack.ss_sp = (char *)signal_callstack_seg->start;
182         signal_callstack.ss_size = signal_callstack_seg->size;
183         signal_callstack.ss_flags = 0;
184
185         if(sigaltstack(&signal_callstack,(stack_t *)NULL) < 0)
186                 fatal_error("sigaltstack() failed",0);
187 #endif
188
189         struct sigaction memory_sigaction;
190         struct sigaction misc_sigaction;
191         struct sigaction fpe_sigaction;
192         struct sigaction ignore_sigaction;
193
194         memset(&memory_sigaction,0,sizeof(struct sigaction));
195         sigemptyset(&memory_sigaction.sa_mask);
196         memory_sigaction.sa_sigaction = memory_signal_handler;
197         memory_sigaction.sa_flags = SA_SIGINFO | SA_ONSTACK;
198
199         sigaction_safe(SIGBUS,&memory_sigaction,NULL);
200         sigaction_safe(SIGSEGV,&memory_sigaction,NULL);
201
202         memset(&fpe_sigaction,0,sizeof(struct sigaction));
203         sigemptyset(&fpe_sigaction.sa_mask);
204         fpe_sigaction.sa_sigaction = fpe_signal_handler;
205         fpe_sigaction.sa_flags = SA_SIGINFO | SA_ONSTACK;
206
207         sigaction_safe(SIGFPE,&fpe_sigaction,NULL);
208
209         memset(&misc_sigaction,0,sizeof(struct sigaction));
210         sigemptyset(&misc_sigaction.sa_mask);
211         misc_sigaction.sa_sigaction = misc_signal_handler;
212         misc_sigaction.sa_flags = SA_SIGINFO | SA_ONSTACK;
213
214         sigaction_safe(SIGQUIT,&misc_sigaction,NULL);
215         sigaction_safe(SIGILL,&misc_sigaction,NULL);
216
217         memset(&ignore_sigaction,0,sizeof(struct sigaction));
218         sigemptyset(&ignore_sigaction.sa_mask);
219         ignore_sigaction.sa_handler = SIG_IGN;
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 }