summaryrefslogtreecommitdiff
path: root/lib/libpthread/pthread_mutex.c
diff options
context:
space:
mode:
authorad <ad@NetBSD.org>2007-08-04 13:37:48 +0000
committerad <ad@NetBSD.org>2007-08-04 13:37:48 +0000
commit50fa8db4e44b07accbfd5f2821987476be3bf05c (patch)
tree04cfc58059fb658923f9a7b24583dd0b3e4437d1 /lib/libpthread/pthread_mutex.c
parentb108f67c054e0f87f9b653c4d194652a4bfc40cc (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.c128
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;