rumpuser_pth.c revision 1.22 1 /* $NetBSD: rumpuser_pth.c,v 1.22 2013/05/02 16:49:08 pooka Exp $ */
2
3 /*
4 * Copyright (c) 2007-2010 Antti Kantee. All Rights Reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include "rumpuser_port.h"
29
30 #if !defined(lint)
31 __RCSID("$NetBSD: rumpuser_pth.c,v 1.22 2013/05/02 16:49:08 pooka Exp $");
32 #endif /* !lint */
33
34 #include <assert.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <pthread.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <stdint.h>
42 #include <unistd.h>
43
44 #include <rump/rumpuser.h>
45
46 #include "rumpuser_int.h"
47
48 static pthread_key_t curlwpkey;
49
50 struct rumpuser_mtx {
51 pthread_mutex_t pthmtx;
52 struct lwp *owner;
53 int flags;
54 };
55
56 #define RURW_AMWRITER(rw) (rw->writer == rumpuser_get_curlwp() \
57 && rw->readers == -1)
58 #define RURW_HASREAD(rw) (rw->readers > 0)
59
60 #define RURW_SETWRITE(rw) \
61 do { \
62 assert(rw->readers == 0); \
63 rw->writer = rumpuser_get_curlwp(); \
64 rw->readers = -1; \
65 } while (/*CONSTCOND*/0)
66 #define RURW_CLRWRITE(rw) \
67 do { \
68 assert(RURW_AMWRITER(rw)); \
69 rw->readers = 0; \
70 rw->writer = NULL; \
71 } while (/*CONSTCOND*/0)
72 #define RURW_INCREAD(rw) \
73 do { \
74 pthread_spin_lock(&rw->spin); \
75 assert(rw->readers >= 0); \
76 ++(rw)->readers; \
77 pthread_spin_unlock(&rw->spin); \
78 } while (/*CONSTCOND*/0)
79 #define RURW_DECREAD(rw) \
80 do { \
81 pthread_spin_lock(&rw->spin); \
82 assert(rw->readers > 0); \
83 --(rw)->readers; \
84 pthread_spin_unlock(&rw->spin); \
85 } while (/*CONSTCOND*/0)
86
87 struct rumpuser_rw {
88 pthread_rwlock_t pthrw;
89 pthread_spinlock_t spin;
90 int readers;
91 struct lwp *writer;
92 };
93
94 struct rumpuser_cv {
95 pthread_cond_t pthcv;
96 int nwaiters;
97 };
98
99 void
100 rumpuser__thrinit(void)
101 {
102
103 pthread_key_create(&curlwpkey, NULL);
104 }
105
106 int
107 rumpuser_thread_create(void *(*f)(void *), void *arg, const char *thrname,
108 int joinable, int priority, int cpuidx, void **ptcookie)
109 {
110 pthread_t ptid;
111 pthread_t *ptidp;
112 pthread_attr_t pattr;
113 int rv;
114
115 if ((rv = pthread_attr_init(&pattr)) != 0)
116 return rv;
117
118 if (joinable) {
119 NOFAIL(ptidp = malloc(sizeof(*ptidp)));
120 pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_JOINABLE);
121 } else {
122 ptidp = &ptid;
123 pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED);
124 }
125
126 rv = pthread_create(ptidp, &pattr, f, arg);
127 #if defined(__NetBSD__)
128 if (rv == 0 && thrname)
129 pthread_setname_np(ptid, thrname, NULL);
130 #elif defined(__linux__)
131 /*
132 * The pthread_setname_np() call varies from one Linux distro to
133 * another. Comment out the call pending autoconf support.
134 */
135 #if 0
136 if (rv == 0 && thrname)
137 pthread_setname_np(ptid, thrname);
138 #endif
139 #endif
140
141 if (joinable) {
142 assert(ptcookie);
143 *ptcookie = ptidp;
144 }
145
146 pthread_attr_destroy(&pattr);
147
148 ET(rv);
149 }
150
151 __dead void
152 rumpuser_thread_exit(void)
153 {
154
155 pthread_exit(NULL);
156 }
157
158 int
159 rumpuser_thread_join(void *ptcookie)
160 {
161 pthread_t *pt = ptcookie;
162 int rv;
163
164 KLOCK_WRAP((rv = pthread_join(*pt, NULL)));
165 if (rv == 0)
166 free(pt);
167
168 ET(rv);
169 }
170
171 void
172 rumpuser_mutex_init(struct rumpuser_mtx **mtx, int flags)
173 {
174 pthread_mutexattr_t att;
175
176 NOFAIL(*mtx = malloc(sizeof(struct rumpuser_mtx)));
177
178 pthread_mutexattr_init(&att);
179 pthread_mutexattr_settype(&att, PTHREAD_MUTEX_ERRORCHECK);
180 NOFAIL_ERRNO(pthread_mutex_init(&((*mtx)->pthmtx), &att));
181 pthread_mutexattr_destroy(&att);
182
183 (*mtx)->owner = NULL;
184 assert(flags != 0);
185 (*mtx)->flags = flags;
186 }
187
188 static void
189 mtxenter(struct rumpuser_mtx *mtx)
190 {
191
192 if (!(mtx->flags & RUMPUSER_MTX_KMUTEX))
193 return;
194
195 assert(mtx->owner == NULL);
196 mtx->owner = rumpuser_get_curlwp();
197 }
198
199 static void
200 mtxexit(struct rumpuser_mtx *mtx)
201 {
202
203 if (!(mtx->flags & RUMPUSER_MTX_KMUTEX))
204 return;
205
206 assert(mtx->owner != NULL);
207 mtx->owner = NULL;
208 }
209
210 void
211 rumpuser_mutex_enter(struct rumpuser_mtx *mtx)
212 {
213
214 if (mtx->flags & RUMPUSER_MTX_SPIN) {
215 rumpuser_mutex_enter_nowrap(mtx);
216 return;
217 }
218
219 assert(mtx->flags & RUMPUSER_MTX_KMUTEX);
220 if (pthread_mutex_trylock(&mtx->pthmtx) != 0)
221 KLOCK_WRAP(NOFAIL_ERRNO(pthread_mutex_lock(&mtx->pthmtx)));
222 mtxenter(mtx);
223 }
224
225 void
226 rumpuser_mutex_enter_nowrap(struct rumpuser_mtx *mtx)
227 {
228
229 assert(mtx->flags & RUMPUSER_MTX_SPIN);
230 NOFAIL_ERRNO(pthread_mutex_lock(&mtx->pthmtx));
231 mtxenter(mtx);
232 }
233
234 int
235 rumpuser_mutex_tryenter(struct rumpuser_mtx *mtx)
236 {
237 int rv;
238
239 rv = pthread_mutex_trylock(&mtx->pthmtx);
240 if (rv == 0) {
241 mtxenter(mtx);
242 }
243
244 ET(rv);
245 }
246
247 void
248 rumpuser_mutex_exit(struct rumpuser_mtx *mtx)
249 {
250
251 mtxexit(mtx);
252 NOFAIL_ERRNO(pthread_mutex_unlock(&mtx->pthmtx));
253 }
254
255 void
256 rumpuser_mutex_destroy(struct rumpuser_mtx *mtx)
257 {
258
259 NOFAIL_ERRNO(pthread_mutex_destroy(&mtx->pthmtx));
260 free(mtx);
261 }
262
263 void
264 rumpuser_mutex_owner(struct rumpuser_mtx *mtx, struct lwp **lp)
265 {
266
267 if (__predict_false(!(mtx->flags & RUMPUSER_MTX_KMUTEX))) {
268 printf("panic: rumpuser_mutex_held unsupported on non-kmtx\n");
269 abort();
270 }
271
272 *lp = mtx->owner;
273 }
274
275 void
276 rumpuser_rw_init(struct rumpuser_rw **rw)
277 {
278
279 NOFAIL(*rw = malloc(sizeof(struct rumpuser_rw)));
280 NOFAIL_ERRNO(pthread_rwlock_init(&((*rw)->pthrw), NULL));
281 NOFAIL_ERRNO(pthread_spin_init(&((*rw)->spin),PTHREAD_PROCESS_PRIVATE));
282 (*rw)->readers = 0;
283 (*rw)->writer = NULL;
284 }
285
286 void
287 rumpuser_rw_enter(struct rumpuser_rw *rw, int iswrite)
288 {
289
290 if (iswrite) {
291 if (pthread_rwlock_trywrlock(&rw->pthrw) != 0)
292 KLOCK_WRAP(NOFAIL_ERRNO(
293 pthread_rwlock_wrlock(&rw->pthrw)));
294 RURW_SETWRITE(rw);
295 } else {
296 if (pthread_rwlock_tryrdlock(&rw->pthrw) != 0)
297 KLOCK_WRAP(NOFAIL_ERRNO(
298 pthread_rwlock_rdlock(&rw->pthrw)));
299 RURW_INCREAD(rw);
300 }
301 }
302
303 int
304 rumpuser_rw_tryenter(struct rumpuser_rw *rw, int iswrite)
305 {
306 int rv;
307
308 if (iswrite) {
309 rv = pthread_rwlock_trywrlock(&rw->pthrw);
310 if (rv == 0)
311 RURW_SETWRITE(rw);
312 } else {
313 rv = pthread_rwlock_tryrdlock(&rw->pthrw);
314 if (rv == 0)
315 RURW_INCREAD(rw);
316 }
317
318 ET(rv);
319 }
320
321 void
322 rumpuser_rw_exit(struct rumpuser_rw *rw)
323 {
324
325 if (RURW_HASREAD(rw))
326 RURW_DECREAD(rw);
327 else
328 RURW_CLRWRITE(rw);
329 NOFAIL_ERRNO(pthread_rwlock_unlock(&rw->pthrw));
330 }
331
332 void
333 rumpuser_rw_destroy(struct rumpuser_rw *rw)
334 {
335
336 NOFAIL_ERRNO(pthread_rwlock_destroy(&rw->pthrw));
337 NOFAIL_ERRNO(pthread_spin_destroy(&rw->spin));
338 free(rw);
339 }
340
341 void
342 rumpuser_rw_held(struct rumpuser_rw *rw, int *rv)
343 {
344
345 *rv = rw->readers != 0;
346 }
347
348 void
349 rumpuser_rw_rdheld(struct rumpuser_rw *rw, int *rv)
350 {
351
352 *rv = RURW_HASREAD(rw);
353 }
354
355 void
356 rumpuser_rw_wrheld(struct rumpuser_rw *rw, int *rv)
357 {
358
359 *rv = RURW_AMWRITER(rw);
360 }
361
362 void
363 rumpuser_cv_init(struct rumpuser_cv **cv)
364 {
365
366 NOFAIL(*cv = malloc(sizeof(struct rumpuser_cv)));
367 NOFAIL_ERRNO(pthread_cond_init(&((*cv)->pthcv), NULL));
368 (*cv)->nwaiters = 0;
369 }
370
371 void
372 rumpuser_cv_destroy(struct rumpuser_cv *cv)
373 {
374
375 NOFAIL_ERRNO(pthread_cond_destroy(&cv->pthcv));
376 free(cv);
377 }
378
379 void
380 rumpuser_cv_wait(struct rumpuser_cv *cv, struct rumpuser_mtx *mtx)
381 {
382 int nlocks;
383
384 cv->nwaiters++;
385 rumpkern_unsched(&nlocks, mtx);
386 mtxexit(mtx);
387 NOFAIL_ERRNO(pthread_cond_wait(&cv->pthcv, &mtx->pthmtx));
388 mtxenter(mtx);
389 rumpkern_sched(nlocks, mtx);
390 cv->nwaiters--;
391 }
392
393 void
394 rumpuser_cv_wait_nowrap(struct rumpuser_cv *cv, struct rumpuser_mtx *mtx)
395 {
396
397 cv->nwaiters++;
398 mtxexit(mtx);
399 NOFAIL_ERRNO(pthread_cond_wait(&cv->pthcv, &mtx->pthmtx));
400 mtxenter(mtx);
401 cv->nwaiters--;
402 }
403
404 int
405 rumpuser_cv_timedwait(struct rumpuser_cv *cv, struct rumpuser_mtx *mtx,
406 int64_t sec, int64_t nsec)
407 {
408 struct timespec ts;
409 int rv, nlocks;
410
411 /*
412 * Get clock already here, just in case we will be put to sleep
413 * after releasing the kernel context.
414 *
415 * The condition variables should use CLOCK_MONOTONIC, but since
416 * that's not available everywhere, leave it for another day.
417 */
418 clock_gettime(CLOCK_REALTIME, &ts);
419
420 cv->nwaiters++;
421 rumpkern_unsched(&nlocks, mtx);
422 mtxexit(mtx);
423
424 ts.tv_sec += sec;
425 ts.tv_nsec += nsec;
426 if (ts.tv_nsec >= 1000*1000*1000) {
427 ts.tv_sec++;
428 ts.tv_nsec -= 1000*1000*1000;
429 }
430 rv = pthread_cond_timedwait(&cv->pthcv, &mtx->pthmtx, &ts);
431 mtxenter(mtx);
432 rumpkern_sched(nlocks, mtx);
433 cv->nwaiters--;
434
435 ET(rv);
436 }
437
438 void
439 rumpuser_cv_signal(struct rumpuser_cv *cv)
440 {
441
442 NOFAIL_ERRNO(pthread_cond_signal(&cv->pthcv));
443 }
444
445 void
446 rumpuser_cv_broadcast(struct rumpuser_cv *cv)
447 {
448
449 NOFAIL_ERRNO(pthread_cond_broadcast(&cv->pthcv));
450 }
451
452 void
453 rumpuser_cv_has_waiters(struct rumpuser_cv *cv, int *nwaiters)
454 {
455
456 *nwaiters = cv->nwaiters;
457 }
458
459 /*
460 * curlwp
461 */
462
463 void
464 rumpuser_set_curlwp(struct lwp *l)
465 {
466
467 assert(pthread_getspecific(curlwpkey) == NULL || l == NULL);
468 pthread_setspecific(curlwpkey, l);
469 }
470
471 struct lwp *
472 rumpuser_get_curlwp(void)
473 {
474
475 return pthread_getspecific(curlwpkey);
476 }
477