fix signal descriptor initialization
[archipelago] / xseg / drivers / user / xseg_pthread.c
1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <sys/mman.h>
8 #include <sys/syscall.h>
9 #include <fcntl.h>
10 #include <errno.h>
11 #include <string.h>
12 #include <signal.h>
13 #include <sys/util.h>
14 #include <xseg/xseg.h>
15 #include <pthread.h>
16 #include <drivers/xseg_pthread.h>
17 #define ERRSIZE 512
18 char errbuf[ERRSIZE];
19
20 static void *pthread_malloc(uint64_t size);
21 static void pthread_mfree(void *mem);
22
23 static long pthread_allocate(const char *name, uint64_t size)
24 {
25         int fd, r;
26         fd = shm_open(name, O_RDWR | O_CREAT, 0770);
27         if (fd < 0) {
28                 XSEGLOG("Cannot create shared segment: %s\n",
29                         strerror_r(errno, errbuf, ERRSIZE));
30                 return fd;
31         }
32
33         r = lseek(fd, size -1, SEEK_SET);
34         if (r < 0) {
35                 close(fd);
36                 XSEGLOG("Cannot seek into segment file: %s\n",
37                         strerror_r(errno, errbuf, ERRSIZE));
38                 return r;
39         }
40
41         errbuf[0] = 0;
42         r = write(fd, errbuf, 1);
43         if (r != 1) {
44                 close(fd);
45                 XSEGLOG("Failed to set segment size: %s\n",
46                         strerror_r(errno, errbuf, ERRSIZE));
47                 return r;
48         }
49
50         close(fd);
51         return 0;
52 }
53
54 static long pthread_deallocate(const char *name)
55 {
56         return shm_unlink(name);
57 }
58
59 static void *pthread_map(const char *name, uint64_t size, struct xseg *seg)
60 {
61         struct xseg *xseg;
62         int fd;
63
64         if (seg)
65                 XSEGLOG("struct xseg * is not NULL. Ignoring...\n");
66
67         fd = shm_open(name, O_RDWR, 0000);
68         if (fd < 0) {
69                 XSEGLOG("Failed to open '%s' for mapping: %s\n",
70                         name, strerror_r(errno, errbuf, ERRSIZE));
71                 return NULL;
72         }
73
74         xseg = mmap (   XSEG_BASE_AS_PTR,
75                         size,
76                         PROT_READ | PROT_WRITE,
77                         MAP_SHARED | MAP_FIXED /* | MAP_LOCKED */,
78                         fd, 0   );
79
80         if (xseg == MAP_FAILED) {
81                 XSEGLOG("Could not map segment: %s\n",
82                         strerror_r(errno, errbuf, ERRSIZE));
83                 return NULL;
84         }
85
86         close(fd);
87         return xseg;
88 }
89
90 static void pthread_unmap(void *ptr, uint64_t size)
91 {
92         struct xseg *xseg = ptr;
93         (void)munmap(xseg, xseg->segment_size);
94 }
95
96
97 static void handler(int signum)
98 {
99         static unsigned long counter;
100         printf("%lu: signal %d: this shouldn't have happened.\n", counter, signum);
101         counter ++;
102 }
103
104 static pthread_key_t pid_key, xpidx_key;
105 static pthread_key_t mask_key, act_key;
106 static pthread_once_t once_init = PTHREAD_ONCE_INIT;
107 static pthread_once_t once_quit = PTHREAD_ONCE_INIT;
108 static int isInit;
109
110 static void keys_init(void) 
111 {
112         int r;
113         
114         r = pthread_key_create(&pid_key, NULL);
115         if (r < 0) {
116                 isInit = 0;
117                 return;
118         }
119         
120         r = pthread_key_create(&xpidx_key, NULL);
121         if (r < 0) {
122                 isInit = 0;
123                 return;
124         }
125         r = pthread_key_create(&mask_key, NULL);
126         if (r < 0) {
127                 isInit = 0;
128                 return;
129         }
130         
131         r = pthread_key_create(&act_key, NULL);
132         if (r < 0) {
133                 isInit = 0;
134                 return;
135         }
136         isInit = 1;
137         once_quit = PTHREAD_ONCE_INIT;
138 }
139
140 #define INT_TO_POINTER(__myptr, __myint) \
141         do {\
142                 unsigned long __foo__ = (unsigned long) __myint; \
143                 __myptr = (void *) __foo__ ; \
144         } while (0)
145
146 #define POINTER_TO_INT(__myint, __myptr)\
147         do { \
148                 unsigned long __foo__ = (unsigned long) __myptr; \
149                 __myint = (int) __foo__ ; \
150         } while (0)
151
152 /* must be called by each thread */
153 static int pthread_local_signal_init(void)
154 {
155         int r;
156         pid_t pid;
157         void *tmp;
158         sigset_t *savedset, *set;
159         struct sigaction *act, *old_act;
160
161         savedset = pthread_malloc(sizeof(sigset_t));
162         if (!savedset)
163                 goto err1;
164         set = pthread_malloc(sizeof(sigset_t));
165         if (!set)
166                 goto err2;
167
168         act = pthread_malloc(sizeof(struct sigaction));
169         if (!act)
170                 goto err3;
171         old_act = pthread_malloc(sizeof(struct sigaction));
172         if (!old_act)
173                 goto err4;
174
175         pthread_once(&once_init, keys_init);
176         if (!isInit)
177                 goto err5;
178
179         sigemptyset(set);
180         act->sa_handler = handler;
181         act->sa_mask = *set;
182         act->sa_flags = 0;
183         if(sigaction(SIGIO, act, old_act) < 0)
184                 goto err5;
185
186         
187         sigaddset(set, SIGIO);
188
189         r = pthread_sigmask(SIG_BLOCK, set, savedset);
190         if (r < 0) 
191                 goto err6;
192
193
194         pid = syscall(SYS_gettid);
195         INT_TO_POINTER(tmp, pid);
196         if (!pthread_setspecific(pid_key, tmp) ||
197                         pthread_setspecific(mask_key, savedset) ||
198                         pthread_setspecific(act_key, old_act))
199                 goto err7;
200
201         return 0;
202
203 err7:
204         pthread_sigmask(SIG_BLOCK, savedset, NULL);
205 err6:
206         sigaction(SIGIO, old_act, NULL);
207 err5:
208         pthread_mfree(old_act);
209 err4:
210         pthread_mfree(act);
211 err3:
212         pthread_mfree(set);
213 err2:
214         pthread_mfree(savedset);
215 err1:
216         return -1;
217 }
218
219 /* should be called by each thread which had initialized signals */
220 static void pthread_local_signal_quit(void)
221 {
222         sigset_t *savedset;
223         struct sigaction *old_act;
224
225         savedset = pthread_getspecific(act_key);
226         old_act = pthread_getspecific(mask_key);
227         if (old_act)
228                 sigaction(SIGIO, old_act, NULL);
229         if (savedset)
230                 pthread_sigmask(SIG_SETMASK, savedset, NULL);
231 }
232
233 static int pthread_remote_signal_init(void)
234 {
235         return 0;
236 }
237
238 static void pthread_remote_signal_quit(void)
239 {
240         return;
241 }
242
243 static int pthread_prepare_wait(struct xseg *xseg, uint32_t portno)
244 {
245         void * tmp;
246         pid_t pid;
247         xpool_index r;
248         struct xseg_port *port = xseg_get_port(xseg, portno);
249         if (!port) 
250                 return -1;
251         struct pthread_signal_desc *psd = xseg_get_signal_desc(xseg, port);
252         if (!psd)
253                 return -1;
254
255         tmp = pthread_getspecific(pid_key);
256         POINTER_TO_INT(pid, tmp);
257         if (!pid)
258                 return -1;
259
260         r = xpool_add(&psd->waiters, (xpool_index) pid, portno); 
261         if (r == NoIndex)
262                 return -1;
263         pthread_setspecific(xpidx_key, (void *)r);
264         return 0;
265 }
266
267 static int pthread_cancel_wait(struct xseg *xseg, uint32_t portno)
268 {
269         void * tmp;
270         pid_t pid;
271         xpool_data data;
272         xpool_index xpidx, r;
273         struct xseg_port *port = xseg_get_port(xseg, portno);
274         if (!port) 
275                 return -1;
276         struct pthread_signal_desc *psd = xseg_get_signal_desc(xseg, port);
277         if (!psd)
278                 return -1;
279         
280         tmp = pthread_getspecific(pid_key);
281         POINTER_TO_INT(pid, tmp);
282         if (!pid)
283                 return -1;
284
285         xpidx = (xpool_index) pthread_getspecific(xpidx_key);
286
287         r = xpool_remove(&psd->waiters, xpidx, &data, portno);
288         if (r == NoIndex)
289                 return -1;
290         
291         return 0;
292 }
293
294 static int pthread_wait_signal(struct xseg *xseg, uint32_t usec_timeout)
295 {
296         int r;
297         siginfo_t siginfo;
298         struct timespec ts;
299         sigset_t set;
300         sigemptyset(&set);
301         sigaddset(&set, SIGIO);
302
303         ts.tv_sec = usec_timeout / 1000000;
304         ts.tv_nsec = 1000 * (usec_timeout - ts.tv_sec * 1000000);
305
306         r = sigtimedwait(&set, &siginfo, &ts);
307         if (r < 0)
308                 return r;
309
310         return siginfo.si_signo;
311 }
312
313 static int pthread_signal(struct xseg *xseg, uint32_t portno)
314 {
315         xpool_data data;
316         xpool_index idx;
317
318         struct xseg_port *port = xseg_get_port(xseg, portno);
319         if (!port) 
320                 return -1;
321         struct pthread_signal_desc *psd = xseg_get_signal_desc(xseg, port);
322         if (!psd)
323                 return -1;
324
325         idx = xpool_peek(&psd->waiters, &data, portno); //FIXME portno is not the caller but the callee
326         if (idx == NoIndex) 
327                 return 0;
328
329         pid_t cue = (pid_t) data;
330         if (!cue)
331                 return 0;
332
333         return syscall(SYS_tkill, cue, SIGIO);
334 }
335
336 static void *pthread_malloc(uint64_t size)
337 {
338         return malloc((size_t)size);
339 }
340
341 static void *pthread_realloc(void *mem, uint64_t size)
342 {
343         return realloc(mem, (size_t)size);
344 }
345
346 static void pthread_mfree(void *mem)
347 {
348         free(mem);
349 }
350
351 static struct xseg_type xseg_pthread = {
352         /* xseg_operations */
353         {
354                 .mfree          = pthread_mfree,
355                 .allocate       = pthread_allocate,
356                 .deallocate     = pthread_deallocate,
357                 .map            = pthread_map,
358                 .unmap          = pthread_unmap,
359         },
360         /* name */
361         "pthread"
362 };
363
364 int pthread_init_signal_desc(struct xseg *xseg, void *sd)
365 {       
366         struct pthread_signal_desc *psd = (struct pthread_signal_desc *)sd;
367         xpool_init(&psd->waiters, MAX_WAITERS, &psd->bufs);
368         xpool_clear(&psd->waiters, 1);
369         return 0;
370 }       
371
372 void pthread_quit_signal_desc(struct xseg *xseg, void *sd)
373 {       
374         struct pthread_signal_desc *psd = (struct pthread_signal_desc *)sd;
375         xpool_clear(&psd->waiters, 1);
376         return;
377 }           
378
379 void * pthread_alloc_data(struct xseg *xseg)
380 {
381         struct xobject_h *sd_h = xseg_get_objh(xseg, MAGIC_PTHREAD_SD,
382                                 sizeof(struct pthread_signal_desc));
383         return sd_h;
384 }
385
386 void pthread_free_data(struct xseg *xseg, void *data)
387 {
388         if (data)
389                 xseg_put_objh(xseg, (struct xobject_h *)data);
390 }
391
392 void *pthread_alloc_signal_desc(struct xseg *xseg, void *data)
393 {
394         struct xobject_h *sd_h = (struct xobject_h *) data;
395         if (!sd_h)
396                 return NULL;
397         struct pthread_signal_desc *psd = xobj_get_obj(sd_h, X_ALLOC);
398         if (!psd)
399                 return NULL;
400         return psd;
401
402 }
403
404 void pthread_free_signal_desc(struct xseg *xseg, void *data, void *sd)
405 {
406         struct xobject_h *sd_h = (struct xobject_h *) data;
407         if (!sd_h)
408                 return;
409         if (sd)
410                 xobj_put_obj(sd_h, sd);
411         return;
412 }
413
414
415 static struct xseg_peer xseg_peer_pthread = {
416         /* xseg_peer_operations */
417         {
418                 .init_signal_desc   = pthread_init_signal_desc,
419                 .quit_signal_desc   = pthread_quit_signal_desc,
420                 .alloc_data         = pthread_alloc_data,
421                 .free_data          = pthread_free_data,
422                 .alloc_signal_desc  = pthread_alloc_signal_desc,
423                 .free_signal_desc   = pthread_free_signal_desc,
424                 .local_signal_init  = pthread_local_signal_init,
425                 .local_signal_quit  = pthread_local_signal_quit,
426                 .remote_signal_init = pthread_remote_signal_init,
427                 .remote_signal_quit = pthread_remote_signal_quit,
428                 .prepare_wait   = pthread_prepare_wait,
429                 .cancel_wait    = pthread_cancel_wait,
430                 .wait_signal    = pthread_wait_signal,
431                 .signal         = pthread_signal,
432                 .malloc         = pthread_malloc,
433                 .realloc        = pthread_realloc,
434                 .mfree          = pthread_mfree,
435         },
436         /* name */
437         "pthread"
438 };
439
440 void xseg_pthread_init(void)
441 {
442         xseg_register_type(&xseg_pthread);
443         xseg_register_peer(&xseg_peer_pthread);
444 }
445