diff options
| author | ad <ad@NetBSD.org> | 2007-08-04 13:37:48 +0000 |
|---|---|---|
| committer | ad <ad@NetBSD.org> | 2007-08-04 13:37:48 +0000 |
| commit | 50fa8db4e44b07accbfd5f2821987476be3bf05c (patch) | |
| tree | 04cfc58059fb658923f9a7b24583dd0b3e4437d1 /lib/libpthread/pthread_mutex.c | |
| parent | b108f67c054e0f87f9b653c4d194652a4bfc40cc (diff) | |
Some significant performance improvements, and a fix for a race with pthread
detach/join.
- Make mutex acquire spin for a short time, as done with spinlocks.
- Make the number of spins controllable with the env var PTHREAD_NSPINS.
- Reduce the amount of time that libpthread internal spinlocks are held.
- Rely more on the barrier effects of park/unpark to avoid taking spinlocks.
- Simplify the locking around pthreads and the global queues.
- Align per-thread sync data on a 128 byte boundary.
- Offset thread stacks by a small amount to try and reduce cache thrash.
Diffstat (limited to 'lib/libpthread/pthread_mutex.c')
| -rw-r--r-- | lib/libpthread/pthread_mutex.c | 128 |
1 files changed, 64 insertions, 64 deletions
diff --git a/lib/libpthread/pthread_mutex.c b/lib/libpthread/pthread_mutex.c index ee1c6304614..bb9024faf00 100644 --- a/lib/libpthread/pthread_mutex.c +++ b/lib/libpthread/pthread_mutex.c @@ -1,4 +1,4 @@ -/* $NetBSD: pthread_mutex.c,v 1.28 2007/03/24 18:52:00 ad Exp $ */ +/* $NetBSD: pthread_mutex.c,v 1.29 2007/08/04 13:37:49 ad Exp $ */ /*- * Copyright (c) 2001, 2003, 2006, 2007 The NetBSD Foundation, Inc. @@ -37,7 +37,7 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: pthread_mutex.c,v 1.28 2007/03/24 18:52:00 ad Exp $"); +__RCSID("$NetBSD: pthread_mutex.c,v 1.29 2007/08/04 13:37:49 ad Exp $"); #include <errno.h> #include <limits.h> @@ -189,15 +189,26 @@ static int pthread_mutex_lock_slow(pthread_t self, pthread_mutex_t *mutex) { extern int pthread__started; + struct mutex_private *mp; + sigset_t ss; + int count; pthread__error(EINVAL, "Invalid mutex", mutex->ptm_magic == _PT_MUTEX_MAGIC); PTHREADD_ADD(PTHREADD_MUTEX_LOCK_SLOW); - while (/*CONSTCOND*/1) { - if (pthread__simple_lock_try(&mutex->ptm_lock)) - break; /* got it! */ - + + for (;;) { + /* Spin for a while. */ + count = pthread__nspins; + while (mutex->ptm_lock == __SIMPLELOCK_LOCKED && --count > 0) + pthread__smt_pause(); + if (count > 0) { + if (pthread__simple_lock_try(&mutex->ptm_lock) != 0) + break; + continue; + } + /* Okay, didn't look free. Get the interlock... */ pthread_spinlock(self, &mutex->ptm_interlock); @@ -209,71 +220,60 @@ pthread_mutex_lock_slow(pthread_t self, pthread_mutex_t *mutex) * again. */ PTQ_INSERT_HEAD(&mutex->ptm_blocked, self, pt_sleep); - if (mutex->ptm_lock == __SIMPLELOCK_LOCKED) { - struct mutex_private *mp; - - GET_MUTEX_PRIVATE(mutex, mp); - - if (mutex->ptm_owner == self) { - switch (mp->type) { - case PTHREAD_MUTEX_ERRORCHECK: - PTQ_REMOVE(&mutex->ptm_blocked, self, - pt_sleep); - pthread_spinunlock(self, - &mutex->ptm_interlock); - return EDEADLK; - - case PTHREAD_MUTEX_RECURSIVE: - /* - * It's safe to do this without - * holding the interlock, because - * we only modify it if we know we - * own the mutex. - */ - PTQ_REMOVE(&mutex->ptm_blocked, self, - pt_sleep); - pthread_spinunlock(self, - &mutex->ptm_interlock); - if (mp->recursecount == INT_MAX) - return EAGAIN; - mp->recursecount++; - return 0; - } - } + if (mutex->ptm_lock != __SIMPLELOCK_LOCKED) { + PTQ_REMOVE(&mutex->ptm_blocked, self, pt_sleep); + pthread_spinunlock(self, &mutex->ptm_interlock); + continue; + } + + GET_MUTEX_PRIVATE(mutex, mp); - if (pthread__started == 0) { - sigset_t ss; + if (mutex->ptm_owner == self) { + switch (mp->type) { + case PTHREAD_MUTEX_ERRORCHECK: + PTQ_REMOVE(&mutex->ptm_blocked, self, pt_sleep); + pthread_spinunlock(self, &mutex->ptm_interlock); + return EDEADLK; + case PTHREAD_MUTEX_RECURSIVE: /* - * The spec says we must deadlock, so... + * It's safe to do this without + * holding the interlock, because + * we only modify it if we know we + * own the mutex. */ - pthread__assert(mp->type == - PTHREAD_MUTEX_NORMAL); - (void) sigprocmask(SIG_SETMASK, NULL, &ss); - for (;;) { - sigsuspend(&ss); - } - /*NOTREACHED*/ + PTQ_REMOVE(&mutex->ptm_blocked, self, pt_sleep); + pthread_spinunlock(self, &mutex->ptm_interlock); + if (mp->recursecount == INT_MAX) + return EAGAIN; + mp->recursecount++; + return 0; } + } - /* - * Locking a mutex is not a cancellation - * point, so we don't need to do the - * test-cancellation dance. We may get woken - * up spuriously by pthread_cancel or signals, - * but it's okay since we're just going to - * retry. - */ - self->pt_sleeponq = 1; - self->pt_sleepobj = &mutex->ptm_blocked; - (void)pthread__park(self, &mutex->ptm_interlock, - &mutex->ptm_blocked, NULL, 0, &mutex->ptm_blocked); - pthread_spinunlock(self, &mutex->ptm_interlock); - } else { - PTQ_REMOVE(&mutex->ptm_blocked, self, pt_sleep); - pthread_spinunlock(self, &mutex->ptm_interlock); + if (pthread__started == 0) { + /* The spec says we must deadlock, so... */ + pthread__assert(mp->type == PTHREAD_MUTEX_NORMAL); + (void) sigprocmask(SIG_SETMASK, NULL, &ss); + for (;;) { + sigsuspend(&ss); + } + /*NOTREACHED*/ } - /* Go around for another try. */ + + /* + * Locking a mutex is not a cancellation + * point, so we don't need to do the + * test-cancellation dance. We may get woken + * up spuriously by pthread_cancel or signals, + * but it's okay since we're just going to + * retry. + */ + self->pt_sleeponq = 1; + self->pt_sleepobj = &mutex->ptm_blocked; + pthread_spinunlock(self, &mutex->ptm_interlock); + (void)pthread__park(self, &mutex->ptm_interlock, + &mutex->ptm_blocked, NULL, 0, &mutex->ptm_blocked); } return 0; |
