add debug fprintf messages
[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
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
252         tmp = pthread_getspecific(pid_key);
253         POINTER_TO_INT(pid, tmp);
254         if (!pid)
255                 return -1;
256
257         r = xpool_add(&port->waiters, (xpool_index) pid, portno); 
258         if (r == NoIndex)
259                 return -1;
260         pthread_setspecific(xpidx_key, (void *)r);
261         return 0;
262 }
263
264 static int pthread_cancel_wait(struct xseg *xseg, uint32_t portno)
265 {
266         void * tmp;
267         pid_t pid;
268         xpool_data data;
269         xpool_index xpidx, r;
270         struct xseg_port *port = xseg_get_port(xseg, portno);
271         if (!port) 
272                 return -1;
273         
274         tmp = pthread_getspecific(pid_key);
275         POINTER_TO_INT(pid, tmp);
276         if (!pid)
277                 return -1;
278
279         xpidx = (xpool_index) pthread_getspecific(xpidx_key);
280
281         r = xpool_remove(&port->waiters, xpidx, &data, portno);
282         if (r == NoIndex)
283                 return -1;
284         
285         return 0;
286 }
287
288 static int pthread_wait_signal(struct xseg *xseg, uint32_t usec_timeout)
289 {
290         int r;
291         siginfo_t siginfo;
292         struct timespec ts;
293         sigset_t set;
294         sigemptyset(&set);
295         sigaddset(&set, SIGIO);
296
297         ts.tv_sec = usec_timeout / 1000000;
298         ts.tv_nsec = 1000 * (usec_timeout - ts.tv_sec * 1000000);
299
300         r = sigtimedwait(&set, &siginfo, &ts);
301         if (r < 0)
302                 return r;
303
304         return siginfo.si_signo;
305 }
306
307 static int pthread_signal(struct xseg *xseg, uint32_t portno)
308 {
309         xpool_data data;
310         xpool_index idx;
311
312         struct xseg_port *port = xseg_get_port(xseg, portno);
313         if (!port) 
314                 return -1;
315
316         idx = xpool_peek(&port->waiters, &data, portno); //FIXME portno is not the caller but the callee
317         if (idx == NoIndex) 
318                 return 0;
319
320         pid_t cue = (pid_t) data;
321         if (!cue)
322                 return 0;
323
324         return syscall(SYS_tkill, cue, SIGIO);
325 }
326
327 static void *pthread_malloc(uint64_t size)
328 {
329         return malloc((size_t)size);
330 }
331
332 static void *pthread_realloc(void *mem, uint64_t size)
333 {
334         return realloc(mem, (size_t)size);
335 }
336
337 static void pthread_mfree(void *mem)
338 {
339         free(mem);
340 }
341
342 static struct xseg_type xseg_pthread = {
343         /* xseg_operations */
344         {
345                 .mfree          = pthread_mfree,
346                 .allocate       = pthread_allocate,
347                 .deallocate     = pthread_deallocate,
348                 .map            = pthread_map,
349                 .unmap          = pthread_unmap,
350         },
351         /* name */
352         "pthread"
353 };
354
355 static struct xseg_peer xseg_peer_pthread = {
356         /* xseg_peer_operations */
357         {
358                 .local_signal_init  = pthread_local_signal_init,
359                 .local_signal_quit  = pthread_local_signal_quit,
360                 .remote_signal_init = pthread_remote_signal_init,
361                 .remote_signal_quit = pthread_remote_signal_quit,
362                 .prepare_wait   = pthread_prepare_wait,
363                 .cancel_wait    = pthread_cancel_wait,
364                 .wait_signal    = pthread_wait_signal,
365                 .signal         = pthread_signal,
366                 .malloc         = pthread_malloc,
367                 .realloc        = pthread_realloc,
368                 .mfree          = pthread_mfree,
369         },
370         /* name */
371         "pthread"
372 };
373
374 void xseg_pthread_init(void)
375 {
376         xseg_register_type(&xseg_pthread);
377         xseg_register_peer(&xseg_peer_pthread);
378 }
379