summaryrefslogtreecommitdiffstats
path: root/lib/libpthread/thread/thr_cond.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libpthread/thread/thr_cond.c')
-rw-r--r--lib/libpthread/thread/thr_cond.c220
1 files changed, 118 insertions, 102 deletions
diff --git a/lib/libpthread/thread/thr_cond.c b/lib/libpthread/thread/thr_cond.c
index fd7b6d9..cd719d4 100644
--- a/lib/libpthread/thread/thr_cond.c
+++ b/lib/libpthread/thread/thr_cond.c
@@ -47,6 +47,9 @@
static inline struct pthread *cond_queue_deq(pthread_cond_t);
static inline void cond_queue_remove(pthread_cond_t, pthread_t);
static inline void cond_queue_enq(pthread_cond_t, pthread_t);
+static void cond_wait_backout(void *);
+static inline void check_continuation(struct pthread *,
+ struct pthread_cond *, pthread_mutex_t *);
/*
* Double underscore versions are cancellation points. Single underscore
@@ -171,8 +174,7 @@ _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
struct pthread *curthread = _get_curthread();
int rval = 0;
int done = 0;
- int interrupted = 0;
- int unlock_mutex = 1;
+ int mutex_locked = 1;
int seqno;
if (cond == NULL)
@@ -198,10 +200,11 @@ _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
* and backed out of the waiting queue prior to executing the
* signal handler.
*/
- do {
- /* Lock the condition variable structure: */
- THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
+ /* Lock the condition variable structure: */
+ THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
+ seqno = (*cond)->c_seqno;
+ do {
/*
* If the condvar was statically allocated, properly
* initialize the tail queue.
@@ -217,9 +220,6 @@ _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
case COND_TYPE_FAST:
if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
((*cond)->c_mutex != *mutex))) {
- /* Unlock the condition variable structure: */
- THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
-
/* Return invalid argument error: */
rval = EINVAL;
} else {
@@ -233,15 +233,11 @@ _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
*/
cond_queue_enq(*cond, curthread);
- /* Remember the mutex and sequence number: */
- (*cond)->c_mutex = *mutex;
- seqno = (*cond)->c_seqno;
-
/* Wait forever: */
curthread->wakeup_time.tv_sec = -1;
/* Unlock the mutex: */
- if ((unlock_mutex != 0) &&
+ if (mutex_locked &&
((rval = _mutex_cv_unlock(mutex)) != 0)) {
/*
* Cannot unlock the mutex, so remove
@@ -249,22 +245,18 @@ _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
* variable queue:
*/
cond_queue_remove(*cond, curthread);
-
- /* Check for no more waiters: */
- if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
- (*cond)->c_mutex = NULL;
-
- /* Unlock the condition variable structure: */
- THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
}
else {
+ /* Remember the mutex: */
+ (*cond)->c_mutex = *mutex;
+
/*
* Don't unlock the mutex the next
* time through the loop (if the
* thread has to be requeued after
* handling a signal).
*/
- unlock_mutex = 0;
+ mutex_locked = 0;
/*
* This thread is active and is in a
@@ -277,6 +269,7 @@ _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
/* Remember the CV: */
curthread->data.cond = *cond;
+ curthread->sigbackout = cond_wait_backout;
THR_SCHED_UNLOCK(curthread, curthread);
/* Unlock the CV structure: */
@@ -286,8 +279,6 @@ _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
/* Schedule the next thread: */
_thr_sched_switch(curthread);
- curthread->data.cond = NULL;
-
/*
* XXX - This really isn't a good check
* since there can be more than one
@@ -299,41 +290,39 @@ _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
* should be sent "as soon as possible".
*/
done = (seqno != (*cond)->c_seqno);
-
- if (THR_IN_SYNCQ(curthread)) {
+ if (done && !THR_IN_CONDQ(curthread)) {
/*
- * Lock the condition variable
- * while removing the thread.
+ * The thread is dequeued, so
+ * it is safe to clear these.
*/
- THR_LOCK_ACQUIRE(curthread,
- &(*cond)->c_lock);
+ curthread->data.cond = NULL;
+ curthread->sigbackout = NULL;
+ check_continuation(curthread,
+ NULL, mutex);
+ return (_mutex_cv_lock(mutex));
+ }
+
+ /* Relock the CV structure: */
+ THR_LOCK_ACQUIRE(curthread,
+ &(*cond)->c_lock);
+
+ /*
+ * Clear these after taking the lock to
+ * prevent a race condition where a
+ * signal can arrive before dequeueing
+ * the thread.
+ */
+ curthread->data.cond = NULL;
+ curthread->sigbackout = NULL;
+ done = (seqno != (*cond)->c_seqno);
+ if (THR_IN_CONDQ(curthread)) {
cond_queue_remove(*cond,
curthread);
/* Check for no more waiters: */
if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
(*cond)->c_mutex = NULL;
-
- THR_LOCK_RELEASE(curthread,
- &(*cond)->c_lock);
- }
-
- /*
- * Save the interrupted flag; locking
- * the mutex may destroy it.
- */
- interrupted = curthread->interrupted;
-
- /*
- * Note that even though this thread may
- * have been canceled, POSIX requires
- * that the mutex be reaquired prior to
- * cancellation.
- */
- if (done || interrupted) {
- rval = _mutex_cv_lock(mutex);
- unlock_mutex = 1;
}
}
}
@@ -341,18 +330,21 @@ _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
/* Trap invalid condition variable types: */
default:
- /* Unlock the condition variable structure: */
- THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
-
/* Return an invalid argument error: */
rval = EINVAL;
break;
}
- if ((interrupted != 0) && (curthread->continuation != NULL))
- curthread->continuation((void *) curthread);
+ check_continuation(curthread, *cond,
+ mutex_locked ? NULL : mutex);
} while ((done == 0) && (rval == 0));
+ /* Unlock the condition variable structure: */
+ THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
+
+ if (mutex_locked == 0)
+ _mutex_cv_lock(mutex);
+
/* Return the completion status: */
return (rval);
}
@@ -378,8 +370,7 @@ _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
struct pthread *curthread = _get_curthread();
int rval = 0;
int done = 0;
- int interrupted = 0;
- int unlock_mutex = 1;
+ int mutex_locked = 1;
int seqno;
THR_ASSERT(curthread->locklevel == 0,
@@ -407,10 +398,11 @@ _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
* and backed out of the waiting queue prior to executing the
* signal handler.
*/
- do {
- /* Lock the condition variable structure: */
- THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
+ /* Lock the condition variable structure: */
+ THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
+ seqno = (*cond)->c_seqno;
+ do {
/*
* If the condvar was statically allocated, properly
* initialize the tail queue.
@@ -428,9 +420,6 @@ _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
((*cond)->c_mutex != *mutex))) {
/* Return invalid argument error: */
rval = EINVAL;
-
- /* Unlock the condition variable structure: */
- THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
} else {
/* Set the wakeup time: */
curthread->wakeup_time.tv_sec = abstime->tv_sec;
@@ -447,12 +436,8 @@ _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
*/
cond_queue_enq(*cond, curthread);
- /* Remember the mutex and sequence number: */
- (*cond)->c_mutex = *mutex;
- seqno = (*cond)->c_seqno;
-
/* Unlock the mutex: */
- if ((unlock_mutex != 0) &&
+ if (mutex_locked &&
((rval = _mutex_cv_unlock(mutex)) != 0)) {
/*
* Cannot unlock the mutex; remove the
@@ -460,21 +445,17 @@ _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
* variable queue:
*/
cond_queue_remove(*cond, curthread);
-
- /* Check for no more waiters: */
- if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
- (*cond)->c_mutex = NULL;
-
- /* Unlock the condition variable structure: */
- THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
} else {
+ /* Remember the mutex: */
+ (*cond)->c_mutex = *mutex;
+
/*
* Don't unlock the mutex the next
* time through the loop (if the
* thread has to be requeued after
* handling a signal).
*/
- unlock_mutex = 0;
+ mutex_locked = 0;
/*
* This thread is active and is in a
@@ -487,6 +468,7 @@ _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
/* Remember the CV: */
curthread->data.cond = *cond;
+ curthread->sigbackout = cond_wait_backout;
THR_SCHED_UNLOCK(curthread, curthread);
/* Unlock the CV structure: */
@@ -496,8 +478,6 @@ _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
/* Schedule the next thread: */
_thr_sched_switch(curthread);
- curthread->data.cond = NULL;
-
/*
* XXX - This really isn't a good check
* since there can be more than one
@@ -509,38 +489,45 @@ _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
* should be sent "as soon as possible".
*/
done = (seqno != (*cond)->c_seqno);
-
- if (THR_IN_CONDQ(curthread)) {
+ if (done && !THR_IN_CONDQ(curthread)) {
/*
- * Lock the condition variable
- * while removing the thread.
+ * The thread is dequeued, so
+ * it is safe to clear these.
*/
- THR_LOCK_ACQUIRE(curthread,
- &(*cond)->c_lock);
+ curthread->data.cond = NULL;
+ curthread->sigbackout = NULL;
+ check_continuation(curthread,
+ NULL, mutex);
+ return (_mutex_cv_lock(mutex));
+ }
+ /* Relock the CV structure: */
+ THR_LOCK_ACQUIRE(curthread,
+ &(*cond)->c_lock);
+
+ /*
+ * Clear these after taking the lock to
+ * prevent a race condition where a
+ * signal can arrive before dequeueing
+ * the thread.
+ */
+ curthread->data.cond = NULL;
+ curthread->sigbackout = NULL;
+
+ done = (seqno != (*cond)->c_seqno);
+
+ if (THR_IN_CONDQ(curthread)) {
cond_queue_remove(*cond,
curthread);
/* Check for no more waiters: */
if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
(*cond)->c_mutex = NULL;
-
- THR_LOCK_RELEASE(curthread,
- &(*cond)->c_lock);
}
- /*
- * Save the interrupted flag; locking
- * the mutex may destroy it.
- */
- interrupted = curthread->interrupted;
if (curthread->timeout != 0) {
/* The wait timedout. */
rval = ETIMEDOUT;
- (void)_mutex_cv_lock(mutex);
- } else if (interrupted || done) {
- rval = _mutex_cv_lock(mutex);
- unlock_mutex = 1;
}
}
}
@@ -548,18 +535,21 @@ _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
/* Trap invalid condition variable types: */
default:
- /* Unlock the condition variable structure: */
- THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
-
/* Return an invalid argument error: */
rval = EINVAL;
break;
}
- if ((interrupted != 0) && (curthread->continuation != NULL))
- curthread->continuation((void *)curthread);
+ check_continuation(curthread, *cond,
+ mutex_locked ? NULL : mutex);
} while ((done == 0) && (rval == 0));
+ /* Unlock the condition variable structure: */
+ THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
+
+ if (mutex_locked == 0)
+ _mutex_cv_lock(mutex);
+
/* Return the completion status: */
return (rval);
}
@@ -615,6 +605,7 @@ _pthread_cond_signal(pthread_cond_t * cond)
!= NULL) {
THR_SCHED_LOCK(curthread, pthread);
cond_queue_remove(*cond, pthread);
+ pthread->sigbackout = NULL;
if ((pthread->kseg == curthread->kseg) &&
(pthread->active_priority >
curthread->active_priority))
@@ -681,6 +672,7 @@ _pthread_cond_broadcast(pthread_cond_t * cond)
!= NULL) {
THR_SCHED_LOCK(curthread, pthread);
cond_queue_remove(*cond, pthread);
+ pthread->sigbackout = NULL;
if ((pthread->kseg == curthread->kseg) &&
(pthread->active_priority >
curthread->active_priority))
@@ -712,9 +704,31 @@ _pthread_cond_broadcast(pthread_cond_t * cond)
__strong_reference(_pthread_cond_broadcast, _thr_cond_broadcast);
-void
-_cond_wait_backout(struct pthread *curthread)
+static inline void
+check_continuation(struct pthread *curthread, struct pthread_cond *cond,
+ pthread_mutex_t *mutex)
+{
+ if ((curthread->interrupted != 0) &&
+ (curthread->continuation != NULL)) {
+ if (cond != NULL)
+ /* Unlock the condition variable structure: */
+ THR_LOCK_RELEASE(curthread, &cond->c_lock);
+ /*
+ * Note that even though this thread may have been
+ * canceled, POSIX requires that the mutex be
+ * reaquired prior to cancellation.
+ */
+ if (mutex != NULL)
+ _mutex_cv_lock(mutex);
+ curthread->continuation((void *) curthread);
+ PANIC("continuation returned in pthread_cond_wait.\n");
+ }
+}
+
+static void
+cond_wait_backout(void *arg)
{
+ struct pthread *curthread = (struct pthread *)arg;
pthread_cond_t cond;
cond = curthread->data.cond;
@@ -740,6 +754,8 @@ _cond_wait_backout(struct pthread *curthread)
/* Unlock the condition variable structure: */
THR_LOCK_RELEASE(curthread, &cond->c_lock);
}
+ /* No need to call this again. */
+ curthread->sigbackout = NULL;
}
/*
OpenPOWER on IntegriCloud