fix lost signals in cond vars
authorRich Felker <dalias@aerifal.cx>
Mon, 26 Sep 2011 16:54:28 +0000 (12:54 -0400)
committerRich Felker <dalias@aerifal.cx>
Mon, 26 Sep 2011 16:54:28 +0000 (12:54 -0400)
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.

src/internal/pthread_impl.h
src/thread/pthread_cond_broadcast.c
src/thread/pthread_cond_timedwait.c

index 63639ec250dd39bd0799dc24f35446e25f6cde2e..bbb4502f089874c1da1be26ae526b0a46168d895 100644 (file)
@@ -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]
index bf6de048c1f83a7ef708964deb42a1cc9d9c6419..9c6a462b781bd5bb6435df47f6890c02c41b2f4a 100644 (file)
@@ -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. */
index e9b5e2fcb82ffa4f76dd1efee77e5b2ca9d287db..e3dc8147c8b3ae89569a6f4d4a2751147ed5d9e4 100644 (file)
@@ -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;