make drivers respect xsegbd's {prepare, cancel}_wait
[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 long pthread_allocate(const char *name, uint64_t size)
21 {
22         int fd, r;
23         fd = shm_open(name, O_RDWR | O_CREAT, 0770);
24         if (fd < 0) {
25                 XSEGLOG("Cannot create shared segment: %s\n",
26                         strerror_r(errno, errbuf, ERRSIZE));
27                 return fd;
28         }
29
30         r = lseek(fd, size -1, SEEK_SET);
31         if (r < 0) {
32                 close(fd);
33                 XSEGLOG("Cannot seek into segment file: %s\n",
34                         strerror_r(errno, errbuf, ERRSIZE));
35                 return r;
36         }
37
38         errbuf[0] = 0;
39         r = write(fd, errbuf, 1);
40         if (r != 1) {
41                 close(fd);
42                 XSEGLOG("Failed to set segment size: %s\n",
43                         strerror_r(errno, errbuf, ERRSIZE));
44                 return r;
45         }
46
47         close(fd);
48         return 0;
49 }
50
51 static long pthread_deallocate(const char *name)
52 {
53         return shm_unlink(name);
54 }
55
56 static void *pthread_map(const char *name, uint64_t size, struct xseg *seg)
57 {
58         struct xseg *xseg;
59         int fd;
60
61         if (seg)
62                 XSEGLOG("struct xseg * is not NULL. Ignoring...\n");
63
64         fd = shm_open(name, O_RDWR, 0000);
65         if (fd < 0) {
66                 XSEGLOG("Failed to open '%s' for mapping: %s\n",
67                         name, strerror_r(errno, errbuf, ERRSIZE));
68                 return NULL;
69         }
70
71         xseg = mmap (   XSEG_BASE_AS_PTR,
72                         size,
73                         PROT_READ | PROT_WRITE,
74                         MAP_SHARED | MAP_FIXED /* | MAP_LOCKED */,
75                         fd, 0   );
76
77         if (xseg == MAP_FAILED) {
78                 XSEGLOG("Could not map segment: %s\n",
79                         strerror_r(errno, errbuf, ERRSIZE));
80                 return NULL;
81         }
82
83         close(fd);
84         return xseg;
85 }
86
87 static void pthread_unmap(void *ptr, uint64_t size)
88 {
89         struct xseg *xseg = ptr;
90         (void)munmap(xseg, xseg->segment_size);
91 }
92
93
94 static void handler(int signum)
95 {
96         static unsigned long counter;
97         printf("%lu: signal %d: this shouldn't have happened.\n", counter, signum);
98         counter ++;
99 }
100
101 static sigset_t savedset, set;
102 static pthread_key_t key;
103 static pthread_once_t once_init = PTHREAD_ONCE_INIT;
104 static pthread_once_t once_quit = PTHREAD_ONCE_INIT;
105 static int isInit;
106
107 static void signal_init(void) 
108 {
109         void (*h)(int);
110         int r;
111         h = signal(SIGIO, handler);
112         if (h == SIG_ERR){
113                 isInit = 0;
114                 return;
115         }
116
117         sigemptyset(&set);
118         sigaddset(&set, SIGIO);
119
120         r = pthread_sigmask(SIG_BLOCK, &set, &savedset);
121         if (r < 0) {
122                 isInit = 0;
123                 return;
124         }
125         
126         r = pthread_key_create(&key, NULL);
127         if (r < 0) {
128                 isInit = 0;
129                 return;
130         }
131         isInit = 1;
132         once_quit = PTHREAD_ONCE_INIT;
133 }
134
135 static void signal_quit(void) 
136 {
137         pthread_key_delete(key);
138         signal(SIGIO, SIG_DFL);
139         pthread_sigmask(SIG_SETMASK, &savedset, NULL);
140         isInit = 0;
141         once_init = PTHREAD_ONCE_INIT;
142 }
143
144 #define INT_TO_POINTER(__myptr, __myint) \
145         do {\
146                 unsigned long __foo__ = (unsigned long) __myint; \
147                 __myptr = (void *) __foo__ ; \
148         } while (0)
149
150 #define POINTER_TO_INT(__myint, __myptr)\
151         do { \
152                 unsigned long __foo__ = (unsigned long) __myptr; \
153                 __myint = (int) __foo__ ; \
154         } while (0)
155
156 /* must be called by each thread */
157 static int pthread_signal_init(void)
158 {
159         int r;
160         pid_t pid;
161         void *tmp;
162         pthread_once(&once_init, signal_init);
163         if (!isInit)
164                 return -1;
165         pid = syscall(SYS_gettid);
166         INT_TO_POINTER(tmp, pid);
167         r = pthread_setspecific(key, tmp);
168         return r;
169 }
170
171 /* can be called by each thread */
172 static void pthread_signal_quit(void)
173 {
174         pthread_once(&once_quit, signal_quit);
175 }
176
177 static int pthread_prepare_wait(struct xseg *xseg, uint32_t portno)
178 {
179         void * tmp;
180         pid_t pid;
181         struct xseg_port *port = xseg_get_port(xseg, portno);
182         if (!port) 
183                 return -1;
184
185         tmp = pthread_getspecific(key);
186         POINTER_TO_INT(pid, tmp);
187         if (!pid)
188                 return -1;
189
190         xpool_add(&port->waiters, (xpool_index) pid, portno); 
191         return 0;
192 }
193
194 static int pthread_cancel_wait(struct xseg *xseg, uint32_t portno)
195 {
196         void * tmp;
197         pid_t pid;
198         xpool_data data;
199         struct xseg_port *port = xseg_get_port(xseg, portno);
200         if (!port) 
201                 return -1;
202         
203         tmp = pthread_getspecific(key);
204         POINTER_TO_INT(pid, tmp);
205         if (!pid)
206                 return -1;
207
208         xpool_remove(&port->waiters, (xpool_index) pid, &data, portno);
209         return 0;
210 }
211
212 static int pthread_wait_signal(struct xseg *xseg, uint32_t usec_timeout)
213 {
214         int r;
215         siginfo_t siginfo;
216         struct timespec ts;
217
218         ts.tv_sec = usec_timeout / 1000000;
219         ts.tv_nsec = 1000 * (usec_timeout - ts.tv_sec * 1000000);
220
221         /* FIXME: Now that pthread signaling is fixed, we could get rid of the timeout
222          * and use a NULL timespec linux-specific)
223          */
224         r = sigtimedwait(&set, &siginfo, &ts);
225         if (r < 0)
226                 return r;
227
228         return siginfo.si_signo;
229 }
230
231 static int pthread_signal(struct xseg *xseg, uint32_t portno)
232 {
233         xpool_data data;
234         xpool_index idx;
235
236         struct xseg_port *port = xseg_get_port(xseg, portno);
237         if (!port) 
238                 return -1;
239
240         idx = xpool_peek(&port->waiters, &data, portno); //FIXME portno is not the caller but the callee
241         if (idx == NoIndex) 
242                 return -1;
243
244         pid_t cue = (pid_t) data;
245         if (!cue)
246                 return 0;
247
248         return syscall(SYS_tkill, cue, SIGIO);
249 }
250
251 static void *pthread_malloc(uint64_t size)
252 {
253         return malloc((size_t)size);
254 }
255
256 static void *pthread_realloc(void *mem, uint64_t size)
257 {
258         return realloc(mem, (size_t)size);
259 }
260
261 static void pthread_mfree(void *mem)
262 {
263         free(mem);
264 }
265
266 static struct xseg_type xseg_pthread = {
267         /* xseg_operations */
268         {
269                 .mfree          = pthread_mfree,
270                 .allocate       = pthread_allocate,
271                 .deallocate     = pthread_deallocate,
272                 .map            = pthread_map,
273                 .unmap          = pthread_unmap,
274         },
275         /* name */
276         "pthread"
277 };
278
279 static struct xseg_peer xseg_peer_pthread = {
280         /* xseg_peer_operations */
281         {
282                 .signal_init    = pthread_signal_init,
283                 .signal_quit    = pthread_signal_quit,
284                 .prepare_wait   = pthread_prepare_wait,
285                 .cancel_wait    = pthread_cancel_wait,
286                 .wait_signal    = pthread_wait_signal,
287                 .signal         = pthread_signal,
288                 .malloc         = pthread_malloc,
289                 .realloc        = pthread_realloc,
290                 .mfree          = pthread_mfree,
291         },
292         /* name */
293         "pthread"
294 };
295
296 void xseg_pthread_init(void)
297 {
298         xseg_register_type(&xseg_pthread);
299         xseg_register_peer(&xseg_peer_pthread);
300 }
301