diff options
| author | ad <ad@NetBSD.org> | 2007-03-20 23:49:58 +0000 |
|---|---|---|
| committer | ad <ad@NetBSD.org> | 2007-03-20 23:49:58 +0000 |
| commit | d68cf1be1a0768d7af3262fcbe5e09d2aac0d8fd (patch) | |
| tree | 2463671b1d0ecb6908803370b98f809161e29307 /lib/libpthread/pthread_cond.c | |
| parent | b0427b61fbb29fb82e61be1e9b045ecabf2df844 (diff) | |
- When signalling waiters, try not to awaken them immediatley. If we hold
the mutex that the waiters are using to synchronise, then transfer them
to the mutex's waiters list so that the wakeup is deferred until release
of the mutex. Improves the timings for CV sleep/wakeup by between 30-100%
in tests conducted locally on a UP system. There can be a penalty for MP
systems when only one thread is being awoken, but in practice I think it
won't be be an issue.
- pthread_signal: search for a thread that does not have a pending wakeup.
Threads can have a pending wakeup and still be on the waiters list if we
clash with an earlier pthread_cond_broadcast().
Diffstat (limited to 'lib/libpthread/pthread_cond.c')
| -rw-r--r-- | lib/libpthread/pthread_cond.c | 96 |
1 files changed, 75 insertions, 21 deletions
diff --git a/lib/libpthread/pthread_cond.c b/lib/libpthread/pthread_cond.c index 34c4d339b18..1421f12aaa6 100644 --- a/lib/libpthread/pthread_cond.c +++ b/lib/libpthread/pthread_cond.c @@ -1,4 +1,4 @@ -/* $NetBSD: pthread_cond.c,v 1.27 2007/03/14 23:34:48 ad Exp $ */ +/* $NetBSD: pthread_cond.c,v 1.28 2007/03/20 23:49:58 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.27 2007/03/14 23:34:48 ad Exp $"); +__RCSID("$NetBSD: pthread_cond.c,v 1.28 2007/03/20 23:49:58 ad Exp $"); #include <errno.h> #include <sys/time.h> @@ -121,24 +121,23 @@ pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) if (__predict_false(self->pt_cancel)) pthread_exit(PTHREAD_CANCELED); pthread_spinlock(self, &cond->ptc_lock); -#ifdef ERRORCHECK if (cond->ptc_mutex == NULL) cond->ptc_mutex = mutex; - else + else { +#ifdef ERRORCHECK pthread__error(EINVAL, "Multiple mutexes used for condition wait", cond->ptc_mutex == mutex); #endif + } PTQ_INSERT_HEAD(&cond->ptc_waiters, self, pt_sleep); self->pt_sleeponq = 1; self->pt_sleepobj = &cond->ptc_waiters; pthread_mutex_unlock(mutex); (void)pthread__park(self, &cond->ptc_lock, &cond->ptc_waiters, NULL, 1); -#ifdef ERRORCHECK if (PTQ_EMPTY(&cond->ptc_waiters)) cond->ptc_mutex = NULL; -#endif pthread_spinunlock(self, &cond->ptc_lock); pthread_mutex_lock(mutex); @@ -181,24 +180,23 @@ pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, if (__predict_false(self->pt_cancel)) pthread_exit(PTHREAD_CANCELED); pthread_spinlock(self, &cond->ptc_lock); -#ifdef ERRORCHECK if (cond->ptc_mutex == NULL) cond->ptc_mutex = mutex; - else + else { +#ifdef ERRORCHECK pthread__error(EINVAL, "Multiple mutexes used for condition wait", cond->ptc_mutex == mutex); #endif + } PTQ_INSERT_HEAD(&cond->ptc_waiters, self, pt_sleep); self->pt_sleeponq = 1; self->pt_sleepobj = &cond->ptc_waiters; pthread_mutex_unlock(mutex); retval = pthread__park(self, &cond->ptc_lock, &cond->ptc_waiters, abstime, 1); -#ifdef ERRORCHECK if (PTQ_EMPTY(&cond->ptc_waiters)) cond->ptc_mutex = NULL; -#endif pthread_spinunlock(self, &cond->ptc_lock); SDPRINTF(("(cond timed wait %p) Woke up on %p, mutex %p\n", @@ -216,6 +214,7 @@ int pthread_cond_signal(pthread_cond_t *cond) { pthread_t self, signaled; + pthread_mutex_t *mutex; pthread__error(EINVAL, "Invalid condition variable", cond->ptc_magic == _PT_COND_MAGIC); @@ -229,18 +228,48 @@ pthread_cond_signal(pthread_cond_t *cond) self = pthread__self(); pthread_spinlock(self, &cond->ptc_lock); - signaled = PTQ_FIRST(&cond->ptc_waiters); - if (signaled != NULL) { - PTQ_REMOVE(&cond->ptc_waiters, signaled, pt_sleep); - if (PTQ_EMPTY(&cond->ptc_waiters)) - cond->ptc_mutex = NULL; + + /* + * Find a thread that is still blocked (no pending wakeup). + * A wakeup can be pending if we have interrupted unpark_all + * as it releases the interlock. + */ + PTQ_FOREACH(signaled, &cond->ptc_waiters, pt_sleep) { + if (signaled->pt_sleepobj != NULL) + break; + } + if (__predict_false(signaled == NULL)) { + cond->ptc_mutex = NULL; + pthread_spinunlock(self, &cond->ptc_lock); + return 0; + } + + /* + * Pull the thread off the queue. + */ + PTQ_REMOVE(&cond->ptc_waiters, signaled, pt_sleep); + mutex = cond->ptc_mutex; + if (PTQ_EMPTY(&cond->ptc_waiters)) + cond->ptc_mutex = NULL; + + /* + * For all valid uses of pthread_cond_signal(), the caller will + * hold the mutex that the target is using to synchronize with. + * To avoid the target awakening and immediatley blocking on the + * mutex, transfer the thread to be awoken to the mutex's waiters + * list. The waiter will be set running when the caller (this + * thread) releases the mutex. + */ + if (self->pt_mutexhint != NULL && self->pt_mutexhint == mutex) { + pthread_spinunlock(self, &cond->ptc_lock); + pthread_spinlock(self, &mutex->ptm_interlock); + PTQ_INSERT_HEAD(&mutex->ptm_blocked, signaled, pt_sleep); + pthread_spinunlock(self, &mutex->ptm_interlock); + } else { pthread__unpark(self, &cond->ptc_lock, &cond->ptc_waiters, signaled); PTHREADD_ADD(PTHREADD_COND_WOKEUP); - return 0; } - cond->ptc_mutex = NULL; - pthread_spinunlock(self, &cond->ptc_lock); return 0; } @@ -249,9 +278,11 @@ pthread_cond_signal(pthread_cond_t *cond) int pthread_cond_broadcast(pthread_cond_t *cond) { - pthread_t self; + pthread_t self, signaled, next; + pthread_mutex_t *mutex; - pthread__error(EINVAL, "Invalid condition variable", cond->ptc_magic == _PT_COND_MAGIC); + pthread__error(EINVAL, "Invalid condition variable", + cond->ptc_magic == _PT_COND_MAGIC); PTHREADD_ADD(PTHREADD_COND_BROADCAST); SDPRINTF(("(cond signal %p) Broadcasting %p\n", @@ -262,8 +293,31 @@ pthread_cond_broadcast(pthread_cond_t *cond) self = pthread__self(); pthread_spinlock(self, &cond->ptc_lock); + mutex = cond->ptc_mutex; cond->ptc_mutex = NULL; - pthread__unpark_all(self, &cond->ptc_lock, &cond->ptc_waiters); + + /* + * Try to transfer waiters to the mutex's waiters list (see + * pthread_cond_signal()). Only transfer waiters for which + * there is no pending wakeup. + */ + if (self->pt_mutexhint != NULL && self->pt_mutexhint == mutex) { + pthread_spinlock(self, &mutex->ptm_interlock); + for (signaled = PTQ_FIRST(&cond->ptc_waiters); + signaled != NULL; + signaled = next) { + next = PTQ_NEXT(signaled, pt_sleep); + if (__predict_false(signaled->pt_sleepobj == NULL)) + continue; + PTQ_REMOVE(&cond->ptc_waiters, signaled, pt_sleep); + PTQ_INSERT_HEAD(&mutex->ptm_blocked, signaled, + pt_sleep); + } + pthread_spinunlock(self, &mutex->ptm_interlock); + pthread_spinunlock(self, &cond->ptc_lock); + } else + pthread__unpark_all(self, &cond->ptc_lock, &cond->ptc_waiters); + PTHREADD_ADD(PTHREADD_COND_WOKEUP); return 0; |
