summaryrefslogtreecommitdiff
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
parentfd142e5ec44aaafffbb8bb4ea41c4288d3fa937a (diff)
downloadmusl-1fa05210100caefc8546746e08358d81739f4b41.tar.gz
musl-1fa05210100caefc8546746e08358d81739f4b41.tar.bz2
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.
-rw-r--r--src/internal/pthread_impl.h1
-rw-r--r--src/thread/pthread_cond_broadcast.c8
-rw-r--r--src/thread/pthread_cond_timedwait.c22
3 files changed, 17 insertions, 14 deletions
diff --git a/src/internal/pthread_impl.h b/src/internal/pthread_impl.h
index 63639ec2..bbb4502f 100644
--- a/src/internal/pthread_impl.h
+++ b/src/internal/pthread_impl.h
@@ -70,6 +70,7 @@ struct __timer {
#define _c_clock __u.__i[4]
#define _c_lock __u.__i[5]
#define _c_lockwait __u.__i[6]
+#define _c_waiters2 __u.__i[7]
#define _rw_lock __u.__i[0]
#define _rw_waiters __u.__i[1]
#define _b_inst __u.__p[0]
diff --git a/src/thread/pthread_cond_broadcast.c b/src/thread/pthread_cond_broadcast.c
index bf6de048..9c6a462b 100644
--- a/src/thread/pthread_cond_broadcast.c
+++ b/src/thread/pthread_cond_broadcast.c
@@ -22,8 +22,12 @@ int pthread_cond_broadcast(pthread_cond_t *c)
m = c->_c_mutex;
/* Move waiter count to the mutex */
- a_fetch_add(&m->_m_waiters, c->_c_waiters);
- a_store(&c->_c_waiters, 0);
+ for (;;) {
+ int w = c->_c_waiters2;
+ a_fetch_add(&m->_m_waiters, w);
+ if (a_cas(&c->_c_waiters2, w, 0) == w) break;
+ a_fetch_add(&m->_m_waiters, -w);
+ }
/* Perform the futex requeue, waking one waiter unless we know
* that the calling thread holds the mutex. */
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;