Home | History | Annotate | Line # | Download | only in librumpuser
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