about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2020-10-30 11:21:06 -0400
committerRich Felker <dalias@aerifal.cx>2020-10-30 11:21:06 -0400
commit27b2fc9d6db956359727a66c262f1e69995660aa (patch)
tree457269a4769dbf8873bd3479fbbbeb5b9f03539f /src
parent7c71792e87691451f2a6b76348e83ad1889f1dcb (diff)
downloadmusl-27b2fc9d6db956359727a66c262f1e69995660aa.tar.gz
musl-27b2fc9d6db956359727a66c262f1e69995660aa.tar.xz
musl-27b2fc9d6db956359727a66c262f1e69995660aa.zip
fix missing-wake regression in pthread_cond_wait
the reasoning in commit 2d0bbe6c788938d1332609c014eeebc1dff966ac was
not entirely correct. while it's true that setting the waiters flag
ensures that the next unlock will perform a wake, it's possible that
the wake is consumed by a mutex waiter that has no relationship with
the condvar wait queue being processed, which then takes the mutex.
when that thread subsequently unlocks, it sees no waiters, and leaves
the rest of the condvar queue stuck.

bring back the waiter count adjustment, but skip it for PI mutexes,
for which a successful lock-after-waiting always sets the waiters bit.
if future changes are made to bring this same waiters-bit contract to
all lock types, this can be reverted.
Diffstat (limited to 'src')
-rw-r--r--src/thread/pthread_cond_timedwait.c5
1 files changed, 5 insertions, 0 deletions
diff --git a/src/thread/pthread_cond_timedwait.c b/src/thread/pthread_cond_timedwait.c
index f5f37af1..a0cd4904 100644
--- a/src/thread/pthread_cond_timedwait.c
+++ b/src/thread/pthread_cond_timedwait.c
@@ -146,12 +146,17 @@ relock:
 
 	if (oldstate == WAITING) goto done;
 
+	if (!node.next && !(m->_m_type & 8))
+		a_inc(&m->_m_waiters);
+
 	/* Unlock the barrier that's holding back the next waiter, and
 	 * either wake it or requeue it to the mutex. */
 	if (node.prev) {
 		int val = m->_m_lock;
 		if (val>0) a_cas(&m->_m_lock, val, val|0x80000000);
 		unlock_requeue(&node.prev->barrier, &m->_m_lock, m->_m_type & (8|128));
+	} else if (!!(m->_m_type & 8)) {
+		a_dec(&m->_m_waiters);		
 	}
 
 	/* Since a signal was consumed, cancellation is not permitted. */