about summary refs log tree commit diff
path: root/src/thread/pthread_cond_timedwait.c
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2011-09-26 12:54:28 -0400
committerRich Felker <dalias@aerifal.cx>2011-09-26 12:54:28 -0400
commit1fa05210100caefc8546746e08358d81739f4b41 (patch)
treef54a70f0e4c82aa215307a3b2f4eb026115b6311 /src/thread/pthread_cond_timedwait.c
parentfd142e5ec44aaafffbb8bb4ea41c4288d3fa937a (diff)
downloadmusl-1fa05210100caefc8546746e08358d81739f4b41.tar.gz
musl-1fa05210100caefc8546746e08358d81739f4b41.tar.xz
musl-1fa05210100caefc8546746e08358d81739f4b41.zip
fix lost signals in cond vars
due to moving waiters from the cond var to the mutex in bcast, these
waiters upon wakeup would steal slots in the count from newer waiters
that had not yet been signaled, preventing the signal function from
taking any action.

to solve the problem, we simply use two separate waiter counts, and so
that the original "total" waiters count is undisturbed by broadcast
and still available for signal.
Diffstat (limited to 'src/thread/pthread_cond_timedwait.c')
-rw-r--r--src/thread/pthread_cond_timedwait.c22
1 files changed, 10 insertions, 12 deletions
diff --git a/src/thread/pthread_cond_timedwait.c b/src/thread/pthread_cond_timedwait.c
index e9b5e2fc..e3dc8147 100644
--- a/src/thread/pthread_cond_timedwait.c
+++ b/src/thread/pthread_cond_timedwait.c
@@ -7,6 +7,8 @@ struct cm {
 
 static void unwait(pthread_cond_t *c, pthread_mutex_t *m)
 {
+	int w;
+
 	/* Removing a waiter is non-trivial if we could be using requeue
 	 * based broadcast signals, due to mutex access issues, etc. */
 
@@ -18,8 +20,10 @@ static void unwait(pthread_cond_t *c, pthread_mutex_t *m)
 	while (a_swap(&c->_c_lock, 1))
 		__wait(&c->_c_lock, &c->_c_lockwait, 1, 1);
 
-	if (c->_c_waiters) c->_c_waiters--;
-	else a_dec(&m->_m_waiters);
+	/* Atomically decrement waiters2 if positive, else mutex waiters. */
+	do w = c->_c_waiters2;
+	while (w && a_cas(&c->_c_waiters2, w, w-1)!=w);
+	if (!w) a_dec(&m->_m_waiters);
 
 	a_store(&c->_c_lock, 0);
 	if (c->_c_lockwait) __wake(&c->_c_lock, 1, 1);
@@ -42,16 +46,10 @@ int pthread_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m, const struct t
 
 	pthread_testcancel();
 
-	if (c->_c_mutex == (void *)-1) {
-		a_inc(&c->_c_waiters);
-	} else {
-		c->_c_mutex = m;
-		while (a_swap(&c->_c_lock, 1))
-			__wait(&c->_c_lock, &c->_c_lockwait, 1, 1);
-		c->_c_waiters++;
-		a_store(&c->_c_lock, 0);
-		if (c->_c_lockwait) __wake(&c->_c_lock, 1, 1);
-	}
+	if (c->_c_mutex != (void *)-1) c->_c_mutex = m;
+
+	a_inc(&c->_c_waiters);
+	a_inc(&c->_c_waiters2);
 
 	seq = c->_c_seq;