diff options
| author | ad <ad@NetBSD.org> | 2007-08-07 19:04:21 +0000 |
|---|---|---|
| committer | ad <ad@NetBSD.org> | 2007-08-07 19:04:21 +0000 |
| commit | c3f8e2ee5591d3f39aa0d01836fa8be176025b29 (patch) | |
| tree | d6454ebf4d9f3cf78b87fd881a7334c170ff6d73 /lib/libpthread/pthread.c | |
| parent | a72ab2ed2c5aa27d26a684c8dfc8677998d579fa (diff) | |
Change the signature of _lwp_park() to accept an lwpid_t and second
hint pointer, but do so in a way that remains compatible with older
pthread libraries. This can be used to wake another thread before the
calling thread goes asleep, saving at least one syscall + involuntary
context switch. This turns out to be a fairly large win on the condvar
benchmarks that I have tried.
Diffstat (limited to 'lib/libpthread/pthread.c')
| -rw-r--r-- | lib/libpthread/pthread.c | 86 |
1 files changed, 74 insertions, 12 deletions
diff --git a/lib/libpthread/pthread.c b/lib/libpthread/pthread.c index 11cc29db6e9..378a2205684 100644 --- a/lib/libpthread/pthread.c +++ b/lib/libpthread/pthread.c @@ -1,4 +1,4 @@ -/* $NetBSD: pthread.c,v 1.71 2007/08/04 18:54:12 ad Exp $ */ +/* $NetBSD: pthread.c,v 1.72 2007/08/07 19:04:21 ad Exp $ */ /*- * Copyright (c) 2001, 2002, 2003, 2006, 2007 The NetBSD Foundation, Inc. @@ -37,7 +37,7 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: pthread.c,v 1.71 2007/08/04 18:54:12 ad Exp $"); +__RCSID("$NetBSD: pthread.c,v 1.72 2007/08/07 19:04:21 ad Exp $"); #include <err.h> #include <errno.h> @@ -89,6 +89,7 @@ static int pthread__diagassert = DIAGASSERT_ABORT | DIAGASSERT_STDERR; int pthread__concurrency, pthread__maxconcurrency, pthread__nspins; int pthread__unpark_max = PTHREAD__UNPARK_MAX; +int pthread__osrevision; int _sys___sigprocmask14(int, const sigset_t *, sigset_t *); @@ -136,6 +137,13 @@ pthread_init(void) if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) err(1, "sysctl(hw.ncpu"); + mib[0] = CTL_KERN; + mib[1] = KERN_OSREV; + + len = sizeof(pthread__osrev); + if (sysctl(mib, 2, &pthread__osrev, &len, NULL, 0) == -1) + err(1, "sysctl(hw.osrevision"); + /* Initialize locks first; they're needed elsewhere. */ pthread__lockprim_init(ncpu); @@ -241,18 +249,24 @@ pthread__initthread(pthread_t t) { t->pt_magic = PT_MAGIC; + t->pt_state = PT_STATE_RUNNING; t->pt_spinlocks = 0; t->pt_exitval = NULL; t->pt_flags = 0; t->pt_cancel = 0; t->pt_errno = 0; - t->pt_state = PT_STATE_RUNNING; + t->pt_name = NULL; + t->pt_willpark = 0; + t->pt_unpark = 0; + t->pt_sleeponq = 0; + t->pt_sleepobj = NULL; + t->pt_signalled = 0; + t->pt_sleepq = NULL; pthread_lockinit(&t->pt_lock); PTQ_INIT(&t->pt_cleanup_stack); PTQ_INIT(&t->pt_joiners); memset(&t->pt_specific, 0, sizeof(int) * PTHREAD_KEYS_MAX); - t->pt_name = NULL; } @@ -926,10 +940,26 @@ pthread__park(pthread_t self, pthread_spin_t *lock, pthread_queue_t *queue, const struct timespec *abstime, int cancelpt, const void *hint) { - int rv; + int rv, error; SDPRINTF(("(pthread__park %p) queue %p enter\n", self, queue)); + /* Clear the willpark flag, since we're about to block. */ + self->pt_willpark = 0; + + /* + * Kernels before 4.99.27 can't park and unpark in one step, + * so take care of it now if on an old kernel. + * + * XXX Remove this check before NetBSD 5.0 is released. + * It's for compatibility with recent -current only. + */ + if (__predict_false(pthread__osrev < 499002700) && + self->pt_unpark != 0) { + _lwp_unpark(self->pt_unpark, self->pt_unparkhint); + self->pt_unpark = 0; + } + /* * Wait until we are awoken by a pending unpark operation, * a signal, an unpark posted after we have gone asleep, @@ -952,10 +982,23 @@ pthread__park(pthread_t self, pthread_spin_t *lock, * this means that modification of pt_sleepobj/onq by another * thread will become globally visible before that thread * schedules an unpark operation on this thread. + * + * Note: the test in the while() statement dodges the park op if + * we have already been awoken, unless there is another thread to + * awaken. This saves a syscall - if we were already awakened, + * the next call to _lwp_park() would need to return early in order + * to eat the previous wakeup. */ rv = 0; - while (self->pt_sleepobj != NULL && rv == 0) { - if (_lwp_park(abstime, NULL, hint) != 0) { + while ((self->pt_sleepobj != NULL || self->pt_unpark != 0) && rv == 0) { + /* + * If we deferred unparking a thread, arrange to + * have _lwp_park() restart it before blocking. + */ + error = _lwp_park(abstime, self->pt_unpark, hint, + self->pt_unparkhint); + self->pt_unpark = 0; + if (error != 0) { switch (rv = errno) { case EINTR: case EALREADY: @@ -1032,12 +1075,21 @@ pthread__unpark(pthread_t self, pthread_spin_t *lock, * operation is set in motion. */ pthread_spinunlock(self, lock); - rv = _lwp_unpark(target->pt_lid, queue); - if (rv != 0 && errno != EALREADY && errno != EINTR) { - SDPRINTF(("(pthread__unpark %p) syscall rv=%d\n", - self, rv)); - OOPS("_lwp_unpark failed"); + /* + * If the calling thread is about to block, defer + * unparking the target until _lwp_park() is called. + */ + if (self->pt_willpark && self->pt_unpark == 0) { + self->pt_unpark = target->pt_lid; + self->pt_unparkhint = queue; + } else { + rv = _lwp_unpark(target->pt_lid, queue); + if (rv != 0 && errno != EALREADY && errno != EINTR) { + SDPRINTF(("(pthread__unpark %p) syscall rv=%d\n", + self, rv)); + OOPS("_lwp_unpark failed"); + } } } @@ -1105,6 +1157,16 @@ pthread__unpark_all(pthread_t self, pthread_spin_t *lock, case 0: return; case 1: + /* + * If the calling thread is about to block, + * defer unparking the target until _lwp_park() + * is called. + */ + if (self->pt_willpark && self->pt_unpark == 0) { + self->pt_unpark = waiters[0]; + self->pt_unparkhint = queue; + return; + } rv = (ssize_t)_lwp_unpark(waiters[0], queue); if (rv != 0 && errno != EALREADY && errno != EINTR) { OOPS("_lwp_unpark failed"); |
