summaryrefslogtreecommitdiff
path: root/lib/libpthread/pthread_cond.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_cond.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_cond.c')
-rw-r--r--lib/libpthread/pthread_cond.c101
1 files changed, 65 insertions, 36 deletions
diff --git a/lib/libpthread/pthread_cond.c b/lib/libpthread/pthread_cond.c
index d6914dadb75..9d32c98160c 100644
--- a/lib/libpthread/pthread_cond.c
+++ b/lib/libpthread/pthread_cond.c
@@ -1,4 +1,4 @@
-/* $NetBSD: pthread_cond.c,v 1.31 2007/04/12 21:36:06 ad Exp $ */
+/* $NetBSD: pthread_cond.c,v 1.32 2007/08/04 13:37:49 ad Exp $ */
/*-
* Copyright (c) 2001, 2006, 2007 The NetBSD Foundation, Inc.
@@ -37,7 +37,7 @@
*/
#include <sys/cdefs.h>
-__RCSID("$NetBSD: pthread_cond.c,v 1.31 2007/04/12 21:36:06 ad Exp $");
+__RCSID("$NetBSD: pthread_cond.c,v 1.32 2007/08/04 13:37:49 ad Exp $");
#include <errno.h>
#include <sys/time.h>
@@ -46,12 +46,6 @@ __RCSID("$NetBSD: pthread_cond.c,v 1.31 2007/04/12 21:36:06 ad Exp $");
#include "pthread.h"
#include "pthread_int.h"
-#ifdef PTHREAD_COND_DEBUG
-#define SDPRINTF(x) DPRINTF(x)
-#else
-#define SDPRINTF(x)
-#endif
-
int _sys_nanosleep(const struct timespec *, struct timespec *);
extern int pthread__started;
@@ -116,10 +110,14 @@ pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
if (__predict_false(pthread__started == 0))
return pthread_cond_wait_nothread(self, mutex, NULL);
- SDPRINTF(("(cond wait %p) Waiting on %p, mutex %p\n",
- self, cond, mutex));
if (__predict_false(self->pt_cancel))
pthread_exit(PTHREAD_CANCELED);
+
+ /*
+ * Note this thread as waiting on the CV. To ensure good
+ * performance it's critical that the spinlock is held for
+ * as short a time as possible - that means no system calls.
+ */
pthread_spinlock(self, &cond->ptc_lock);
if (cond->ptc_mutex == NULL)
cond->ptc_mutex = mutex;
@@ -134,23 +132,40 @@ pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
self->pt_signalled = 0;
self->pt_sleeponq = 1;
self->pt_sleepobj = &cond->ptc_waiters;
+ pthread_spinunlock(self, &cond->ptc_lock);
+
+ /* Once recorded as a waiter release the mutex and sleep. */
pthread_mutex_unlock(mutex);
(void)pthread__park(self, &cond->ptc_lock, &cond->ptc_waiters,
NULL, 1, &mutex->ptm_blocked);
- if (PTQ_EMPTY(&cond->ptc_waiters))
- cond->ptc_mutex = NULL;
- pthread_spinunlock(self, &cond->ptc_lock);
- pthread_mutex_lock(mutex);
+ /*
+ * If we awoke abnormally the waiters list will have been
+ * made empty by the current thread (in pthread__park()),
+ * so we can check the value safely without locking.
+ *
+ * Otherwise, it will have been updated by whichever thread
+ * last issued a wakeup.
+ */
+ if (PTQ_EMPTY(&cond->ptc_waiters) && cond->ptc_mutex != NULL) {
+ pthread_spinlock(self, &cond->ptc_lock);
+ if (PTQ_EMPTY(&cond->ptc_waiters))
+ cond->ptc_mutex = NULL;
+ pthread_spinunlock(self, &cond->ptc_lock);
+ }
+
+ /*
+ * Re-acquire the mutex and return to the caller. If we
+ * have cancelled then exit. POSIX dictates that the mutex
+ * must be held when we action the cancellation.
+ */
+ pthread_mutex_lock(mutex);
if (__predict_false(self->pt_cancel)) {
if (self->pt_signalled)
pthread_cond_signal(cond);
pthread_exit(PTHREAD_CANCELED);
}
- SDPRINTF(("(cond wait %p) Woke up on %p, mutex %p\n",
- self, cond, mutex));
-
return 0;
}
@@ -178,11 +193,14 @@ pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
if (__predict_false(pthread__started == 0))
return pthread_cond_wait_nothread(self, mutex, abstime);
- SDPRINTF(("(cond timed wait %p) Waiting on %p until %d.%06ld\n",
- self, cond, abstime->tv_sec, abstime->tv_nsec/1000));
-
if (__predict_false(self->pt_cancel))
pthread_exit(PTHREAD_CANCELED);
+
+ /*
+ * Note this thread as waiting on the CV. To ensure good
+ * performance it's critical that the spinlock is held for
+ * as short a time as possible - that means no system calls.
+ */
pthread_spinlock(self, &cond->ptc_lock);
if (cond->ptc_mutex == NULL)
cond->ptc_mutex = mutex;
@@ -197,17 +215,33 @@ pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
self->pt_signalled = 0;
self->pt_sleeponq = 1;
self->pt_sleepobj = &cond->ptc_waiters;
+ pthread_spinunlock(self, &cond->ptc_lock);
+
+ /* Once recorded as a waiter release the mutex and sleep. */
pthread_mutex_unlock(mutex);
retval = pthread__park(self, &cond->ptc_lock, &cond->ptc_waiters,
abstime, 1, &mutex->ptm_blocked);
- if (PTQ_EMPTY(&cond->ptc_waiters))
- cond->ptc_mutex = NULL;
- pthread_spinunlock(self, &cond->ptc_lock);
- SDPRINTF(("(cond timed wait %p) Woke up on %p, mutex %p\n",
- self, cond));
- SDPRINTF(("(cond timed wait %p) %s\n",
- self, (retval == ETIMEDOUT) ? "(timed out)" : ""));
+ /*
+ * If we awoke abnormally the waiters list will have been
+ * made empty by the current thread (in pthread__park()),
+ * so we can check the value safely without locking.
+ *
+ * Otherwise, it will have been updated by whichever thread
+ * last issued a wakeup.
+ */
+ if (PTQ_EMPTY(&cond->ptc_waiters) && cond->ptc_mutex != NULL) {
+ pthread_spinlock(self, &cond->ptc_lock);
+ if (PTQ_EMPTY(&cond->ptc_waiters))
+ cond->ptc_mutex = NULL;
+ pthread_spinunlock(self, &cond->ptc_lock);
+ }
+
+ /*
+ * Re-acquire the mutex and return to the caller. If we
+ * have cancelled then exit. POSIX dictates that the mutex
+ * must be held when we action the cancellation.
+ */
pthread_mutex_lock(mutex);
if (__predict_false(self->pt_cancel | retval)) {
if (self->pt_signalled)
@@ -229,9 +263,6 @@ pthread_cond_signal(pthread_cond_t *cond)
cond->ptc_magic == _PT_COND_MAGIC);
PTHREADD_ADD(PTHREADD_COND_SIGNAL);
- SDPRINTF(("(cond signal %p) Signaling %p\n",
- pthread__self(), cond));
-
if (PTQ_EMPTY(&cond->ptc_waiters))
return 0;
@@ -258,10 +289,10 @@ pthread_cond_signal(pthread_cond_t *cond)
*
* After resuming execution, the thread must check to see if it
* has been restarted as a result of pthread_cond_signal(). If it
- * has, but cannot take the wakeup (because of eg a pending Unix
- * signal or timeout) then try to ensure that another thread sees
- * it. This is necessary because there may be multiple waiters,
- * and at least one should take the wakeup if possible.
+ * has, but cannot take the wakeup (because of eg a timeout) then
+ * try to ensure that another thread sees it. This is necessary
+ * because there may be multiple waiters, and at least one should
+ * take the wakeup if possible.
*/
PTQ_REMOVE(&cond->ptc_waiters, signaled, pt_sleep);
mutex = cond->ptc_mutex;
@@ -302,8 +333,6 @@ pthread_cond_broadcast(pthread_cond_t *cond)
cond->ptc_magic == _PT_COND_MAGIC);
PTHREADD_ADD(PTHREADD_COND_BROADCAST);
- SDPRINTF(("(cond signal %p) Broadcasting %p\n",
- pthread__self(), cond));
if (PTQ_EMPTY(&cond->ptc_waiters))
return 0;