aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--linuxthreads/ChangeLog6
-rw-r--r--linuxthreads/condvar.c88
2 files changed, 72 insertions, 22 deletions
diff --git a/linuxthreads/ChangeLog b/linuxthreads/ChangeLog
index a151848cbf..c4247b290c 100644
--- a/linuxthreads/ChangeLog
+++ b/linuxthreads/ChangeLog
@@ -1,6 +1,10 @@
+2000-07-24 Ulrich Drepper <drepper@redhat.com>
+
+ * condvar.c: Handle spurious wakeups. [PR libc/1749].
+
2000-07-21 Ulrich Drepper <drepper@redhat.com>
- * linuxthreads/spinlock.h: If IMPLEMENT_TAS_WITH_CAS is defined use
+ * spinlock.h: If IMPLEMENT_TAS_WITH_CAS is defined use
__compare_and_swap to define testandset.
* linuxthreads/sysdeps/powerpc/pt-machine.h: Add volatile to asms.
Define IMPLEMENT_TAS_WITH_CAS.
diff --git a/linuxthreads/condvar.c b/linuxthreads/condvar.c
index a6a31ca715..ae1cef1ea9 100644
--- a/linuxthreads/condvar.c
+++ b/linuxthreads/condvar.c
@@ -60,6 +60,7 @@ int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
volatile pthread_descr self = thread_self();
pthread_extricate_if extr;
int already_canceled = 0;
+ int spurious_wakeup_count;
/* Check whether the mutex is locked and owned by this thread. */
if (mutex->__m_kind != PTHREAD_MUTEX_TIMED_NP
@@ -72,6 +73,7 @@ int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
extr.pu_extricate_func = cond_extricate_func;
/* Register extrication interface */
+ THREAD_SETMEM(self, p_condvar_avail, 0);
__pthread_set_own_extricate_if(self, &extr);
/* Atomically enqueue thread for waiting, but only if it is not
@@ -96,7 +98,20 @@ int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
pthread_mutex_unlock(mutex);
- suspend(self);
+ spurious_wakeup_count = 0;
+ while (1)
+ {
+ suspend(self);
+ if (THREAD_GETMEM(self, p_condvar_avail) == 0
+ && THREAD_GETMEM(self, p_woken_by_cancel) == 0)
+ {
+ /* Count resumes that don't belong to us. */
+ spurious_wakeup_count++;
+ continue;
+ }
+ break;
+ }
+
__pthread_set_own_extricate_if(self, 0);
/* Check for cancellation again, to provide correct cancellation
@@ -109,6 +124,10 @@ int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
pthread_exit(PTHREAD_CANCELED);
}
+ /* Put back any resumes we caught that don't belong to us. */
+ while (spurious_wakeup_count--)
+ restart(self);
+
pthread_mutex_lock(mutex);
return 0;
}
@@ -121,6 +140,7 @@ pthread_cond_timedwait_relative(pthread_cond_t *cond,
volatile pthread_descr self = thread_self();
int already_canceled = 0;
pthread_extricate_if extr;
+ int spurious_wakeup_count;
/* Check whether the mutex is locked and owned by this thread. */
if (mutex->__m_kind != PTHREAD_MUTEX_TIMED_NP
@@ -133,6 +153,7 @@ pthread_cond_timedwait_relative(pthread_cond_t *cond,
extr.pu_extricate_func = cond_extricate_func;
/* Register extrication interface */
+ THREAD_SETMEM(self, p_condvar_avail, 0);
__pthread_set_own_extricate_if(self, &extr);
/* Enqueue to wait on the condition and check for cancellation. */
@@ -151,26 +172,39 @@ pthread_cond_timedwait_relative(pthread_cond_t *cond,
pthread_mutex_unlock(mutex);
- if (!timedsuspend(self, abstime)) {
- int was_on_queue;
-
- /* __pthread_lock will queue back any spurious restarts that
- may happen to it. */
-
- __pthread_lock(&cond->__c_lock, self);
- was_on_queue = remove_from_queue(&cond->__c_waiting, self);
- __pthread_unlock(&cond->__c_lock);
-
- if (was_on_queue) {
- __pthread_set_own_extricate_if(self, 0);
- pthread_mutex_lock(mutex);
- return ETIMEDOUT;
+ spurious_wakeup_count = 0;
+ while (1)
+ {
+ if (!timedsuspend(self, abstime)) {
+ int was_on_queue;
+
+ /* __pthread_lock will queue back any spurious restarts that
+ may happen to it. */
+
+ __pthread_lock(&cond->__c_lock, self);
+ was_on_queue = remove_from_queue(&cond->__c_waiting, self);
+ __pthread_unlock(&cond->__c_lock);
+
+ if (was_on_queue) {
+ __pthread_set_own_extricate_if(self, 0);
+ pthread_mutex_lock(mutex);
+ return ETIMEDOUT;
+ }
+
+ /* Eat the outstanding restart() from the signaller */
+ suspend(self);
+ }
+
+ if (THREAD_GETMEM(self, p_condvar_avail) == 0
+ && THREAD_GETMEM(self, p_woken_by_cancel) == 0)
+ {
+ /* Count resumes that don't belong to us. */
+ spurious_wakeup_count++;
+ continue;
+ }
+ break;
}
- /* Eat the outstanding restart() from the signaller */
- suspend(self);
- }
-
__pthread_set_own_extricate_if(self, 0);
/* The remaining logic is the same as in other cancellable waits,
@@ -183,6 +217,10 @@ pthread_cond_timedwait_relative(pthread_cond_t *cond,
pthread_exit(PTHREAD_CANCELED);
}
+ /* Put back any resumes we caught that don't belong to us. */
+ while (spurious_wakeup_count--)
+ restart(self);
+
pthread_mutex_lock(mutex);
return 0;
}
@@ -201,7 +239,11 @@ int pthread_cond_signal(pthread_cond_t *cond)
__pthread_lock(&cond->__c_lock, NULL);
th = dequeue(&cond->__c_waiting);
__pthread_unlock(&cond->__c_lock);
- if (th != NULL) restart(th);
+ if (th != NULL) {
+ th->p_condvar_avail = 1;
+ WRITE_MEMORY_BARRIER();
+ restart(th);
+ }
return 0;
}
@@ -215,7 +257,11 @@ int pthread_cond_broadcast(pthread_cond_t *cond)
cond->__c_waiting = NULL;
__pthread_unlock(&cond->__c_lock);
/* Now signal each process in the queue */
- while ((th = dequeue(&tosignal)) != NULL) restart(th);
+ while ((th = dequeue(&tosignal)) != NULL) {
+ th->p_condvar_avail = 1;
+ WRITE_MEMORY_BARRIER();
+ restart(th);
+ }
return 0;
}