Module Name: src Committed By: sborrill Date: Thu Feb 20 13:00:40 UTC 2014
Modified Files: src/lib/libpthread [netbsd-6]: pthread_cond.c pthread_mutex.c Log Message: Pull up the following revisions(s) (requested by prlw1 in ticket #1029): lib/libpthread/pthread_cond.c: revision 1.62 lib/libpthread/pthread_mutex.c: revision 1.57,1.59 Partial fix for thread deadlock commonly observed with named. Also address PR/44756. To generate a diff of this commit: cvs rdiff -u -r1.56.8.3 -r1.56.8.4 src/lib/libpthread/pthread_cond.c cvs rdiff -u -r1.51.22.1 -r1.51.22.2 src/lib/libpthread/pthread_mutex.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/lib/libpthread/pthread_cond.c diff -u src/lib/libpthread/pthread_cond.c:1.56.8.3 src/lib/libpthread/pthread_cond.c:1.56.8.4 --- src/lib/libpthread/pthread_cond.c:1.56.8.3 Mon Apr 29 01:50:18 2013 +++ src/lib/libpthread/pthread_cond.c Thu Feb 20 13:00:40 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: pthread_cond.c,v 1.56.8.3 2013/04/29 01:50:18 riz Exp $ */ +/* $NetBSD: pthread_cond.c,v 1.56.8.4 2014/02/20 13:00:40 sborrill Exp $ */ /*- * Copyright (c) 2001, 2006, 2007, 2008 The NetBSD Foundation, Inc. @@ -46,7 +46,7 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: pthread_cond.c,v 1.56.8.3 2013/04/29 01:50:18 riz Exp $"); +__RCSID("$NetBSD: pthread_cond.c,v 1.56.8.4 2014/02/20 13:00:40 sborrill Exp $"); #include <errno.h> #include <sys/time.h> @@ -181,10 +181,12 @@ pthread_cond_timedwait(pthread_cond_t *c pthread_mutex_unlock(mutex); self->pt_willpark = 0; self->pt_blocking++; - retval = _lwp_park(abstime, self->pt_unpark, - __UNVOLATILE(&mutex->ptm_waiters), - __UNVOLATILE(&mutex->ptm_waiters)); - self->pt_unpark = 0; + do { + retval = _lwp_park(abstime, self->pt_unpark, + __UNVOLATILE(&mutex->ptm_waiters), + __UNVOLATILE(&mutex->ptm_waiters)); + self->pt_unpark = 0; + } while (retval == -1 && errno == ESRCH); self->pt_blocking--; membar_sync(); pthread_mutex_lock(mutex); Index: src/lib/libpthread/pthread_mutex.c diff -u src/lib/libpthread/pthread_mutex.c:1.51.22.1 src/lib/libpthread/pthread_mutex.c:1.51.22.2 --- src/lib/libpthread/pthread_mutex.c:1.51.22.1 Mon Apr 29 01:50:18 2013 +++ src/lib/libpthread/pthread_mutex.c Thu Feb 20 13:00:40 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: pthread_mutex.c,v 1.51.22.1 2013/04/29 01:50:18 riz Exp $ */ +/* $NetBSD: pthread_mutex.c,v 1.51.22.2 2014/02/20 13:00:40 sborrill Exp $ */ /*- * Copyright (c) 2001, 2003, 2006, 2007, 2008 The NetBSD Foundation, Inc. @@ -47,7 +47,7 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: pthread_mutex.c,v 1.51.22.1 2013/04/29 01:50:18 riz Exp $"); +__RCSID("$NetBSD: pthread_mutex.c,v 1.51.22.2 2014/02/20 13:00:40 sborrill Exp $"); #include <sys/types.h> #include <sys/lwpctl.h> @@ -211,11 +211,61 @@ pthread__mutex_spin(pthread_mutex_t *ptm return owner; } +NOINLINE static void +pthread__mutex_setwaiters(pthread_t self, pthread_mutex_t *ptm) +{ + void *new, *owner; + + /* + * Note that the mutex can become unlocked before we set + * the waiters bit. If that happens it's not safe to sleep + * as we may never be awoken: we must remove the current + * thread from the waiters list and try again. + * + * Because we are doing this atomically, we can't remove + * one waiter: we must remove all waiters and awken them, + * then sleep in _lwp_park() until we have been awoken. + * + * Issue a memory barrier to ensure that we are reading + * the value of ptm_owner/pt_mutexwait after we have entered + * the waiters list (the CAS itself must be atomic). + */ +again: + membar_consumer(); + owner = ptm->ptm_owner; + + if (MUTEX_OWNER(owner) == 0) { + pthread__mutex_wakeup(self, ptm); + return; + } + if (!MUTEX_HAS_WAITERS(owner)) { + new = (void *)((uintptr_t)owner | MUTEX_WAITERS_BIT); + if (atomic_cas_ptr(&ptm->ptm_owner, owner, new) != owner) { + goto again; + } + } + + /* + * Note that pthread_mutex_unlock() can do a non-interlocked CAS. + * We cannot know if the presence of the waiters bit is stable + * while the holding thread is running. There are many assumptions; + * see sys/kern/kern_mutex.c for details. In short, we must spin if + * we see that the holder is running again. + */ + membar_sync(); + pthread__mutex_spin(ptm, owner); + + if (membar_consumer(), !MUTEX_HAS_WAITERS(ptm->ptm_owner)) { + goto again; + } +} + NOINLINE static int pthread__mutex_lock_slow(pthread_mutex_t *ptm) { void *waiters, *new, *owner, *next; pthread_t self; + int serrno; pthread__error(EINVAL, "Invalid mutex", ptm->ptm_magic == _PT_MUTEX_MAGIC); @@ -235,6 +285,7 @@ pthread__mutex_lock_slow(pthread_mutex_t return EDEADLK; } + serrno = errno; for (;; owner = ptm->ptm_owner) { /* Spin while the owner is running. */ owner = pthread__mutex_spin(ptm, owner); @@ -247,6 +298,7 @@ pthread__mutex_lock_slow(pthread_mutex_t next = atomic_cas_ptr(&ptm->ptm_owner, owner, new); if (next == owner) { + errno = serrno; #ifndef PTHREAD__ATOMIC_IS_MEMBAR membar_enter(); #endif @@ -277,48 +329,8 @@ pthread__mutex_lock_slow(pthread_mutex_t break; } - /* - * Set the waiters bit and block. - * - * Note that the mutex can become unlocked before we set - * the waiters bit. If that happens it's not safe to sleep - * as we may never be awoken: we must remove the current - * thread from the waiters list and try again. - * - * Because we are doing this atomically, we can't remove - * one waiter: we must remove all waiters and awken them, - * then sleep in _lwp_park() until we have been awoken. - * - * Issue a memory barrier to ensure that we are reading - * the value of ptm_owner/pt_mutexwait after we have entered - * the waiters list (the CAS itself must be atomic). - */ - membar_consumer(); - for (owner = ptm->ptm_owner;; owner = next) { - if (MUTEX_HAS_WAITERS(owner)) - break; - if (MUTEX_OWNER(owner) == 0) { - pthread__mutex_wakeup(self, ptm); - break; - } - new = (void *)((uintptr_t)owner | MUTEX_WAITERS_BIT); - next = atomic_cas_ptr(&ptm->ptm_owner, owner, new); - if (next == owner) { - /* - * pthread_mutex_unlock() can do a - * non-interlocked CAS. We cannot - * know if our attempt to set the - * waiters bit has succeeded while - * the holding thread is running. - * There are many assumptions; see - * sys/kern/kern_mutex.c for details. - * In short, we must spin if we see - * that the holder is running again. - */ - membar_sync(); - next = pthread__mutex_spin(ptm, owner); - } - } + /* Set the waiters bit and block. */ + pthread__mutex_setwaiters(self, ptm); /* * We may have been awoken by the current thread above,