From 4b153ac42428447a148e6da543ebe6df017078db Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Thu, 22 Sep 2011 21:08:55 -0400 Subject: fix deadlock in condition wait whenever there are multiple waiters it's amazing none of the conformance tests i've run even bothered to check whether something so basic works... --- src/internal/pthread_impl.h | 1 + src/thread/pthread_cond_broadcast.c | 3 ++- src/thread/pthread_cond_signal.c | 3 ++- src/thread/pthread_cond_timedwait.c | 16 +++++++++++++--- 4 files changed, 18 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/internal/pthread_impl.h b/src/internal/pthread_impl.h index 26164d83..d123e5e3 100644 --- a/src/internal/pthread_impl.h +++ b/src/internal/pthread_impl.h @@ -66,6 +66,7 @@ struct __timer { #define _m_count __u.__i[5] #define _c_block __u.__i[0] #define _c_clock __u.__i[1] +#define _c_waiters __u.__i[2] #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 30f7f6df..6002c535 100644 --- a/src/thread/pthread_cond_broadcast.c +++ b/src/thread/pthread_cond_broadcast.c @@ -2,7 +2,8 @@ int pthread_cond_broadcast(pthread_cond_t *c) { - if (a_swap(&c->_c_block, 0)) + int w = c->_c_waiters; + if (a_swap(&c->_c_block, 0) || w) __wake(&c->_c_block, -1, 0); return 0; } diff --git a/src/thread/pthread_cond_signal.c b/src/thread/pthread_cond_signal.c index a0211287..e8ed71cc 100644 --- a/src/thread/pthread_cond_signal.c +++ b/src/thread/pthread_cond_signal.c @@ -2,7 +2,8 @@ int pthread_cond_signal(pthread_cond_t *c) { - if (a_swap(&c->_c_block, 0)); + int w = c->_c_waiters; + if (a_swap(&c->_c_block, 0) || w) __wake(&c->_c_block, 1, 0); return 0; } diff --git a/src/thread/pthread_cond_timedwait.c b/src/thread/pthread_cond_timedwait.c index ee874a36..ec5aa6f4 100644 --- a/src/thread/pthread_cond_timedwait.c +++ b/src/thread/pthread_cond_timedwait.c @@ -1,12 +1,20 @@ #include "pthread_impl.h" -static void relock(void *m) +struct cm { + pthread_cond_t *c; + pthread_mutex_t *m; +}; + +static void cleanup(void *p) { - pthread_mutex_lock(m); + struct cm *cm = p; + a_dec(&cm->c->_c_waiters); + pthread_mutex_lock(cm->m); } int pthread_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m, const struct timespec *ts) { + struct cm cm = { .c=c, .m=m }; int r, e=0; if (ts && ts->tv_nsec >= 1000000000UL) @@ -17,8 +25,10 @@ int pthread_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m, const struct t c->_c_block = 1; if ((r=pthread_mutex_unlock(m))) return r; - do e = __timedwait(&c->_c_block, 1, c->_c_clock, ts, relock, m, 0); + a_inc(&c->_c_waiters); + do e = __timedwait(&c->_c_block, 1, c->_c_clock, ts, cleanup, &cm, 0); while (e == EINTR); + a_dec(&c->_c_waiters); if ((r=pthread_mutex_lock(m))) return r; -- cgit v1.2.3-70-g09d2