aboutsummaryrefslogtreecommitdiff
path: root/linuxthreads/spinlock.c
diff options
context:
space:
mode:
Diffstat (limited to 'linuxthreads/spinlock.c')
-rw-r--r--linuxthreads/spinlock.c24
1 files changed, 23 insertions, 1 deletions
diff --git a/linuxthreads/spinlock.c b/linuxthreads/spinlock.c
index ce6ff9e310..b1a99d9753 100644
--- a/linuxthreads/spinlock.c
+++ b/linuxthreads/spinlock.c
@@ -40,6 +40,7 @@ void internal_function __pthread_lock(struct _pthread_fastlock * lock,
pthread_descr self)
{
long oldstatus, newstatus;
+ int spurious_wakeup_count = 0;
do {
oldstatus = lock->__status;
@@ -56,7 +57,28 @@ void internal_function __pthread_lock(struct _pthread_fastlock * lock,
}
} while(! compare_and_swap(&lock->__status, oldstatus, newstatus,
&lock->__spinlock));
- if (oldstatus != 0) suspend(self);
+
+ /* Suspend with guard against spurious wakeup.
+ This can happen in pthread_cond_timedwait_relative, when the thread
+ wakes up due to timeout and is still on the condvar queue, and then
+ locks the queue to remove itself. At that point it may still be on the
+ queue, and may be resumed by a condition signal. */
+
+ if (oldstatus != 0) {
+ for (;;) {
+ suspend(self);
+ if (self->p_nextlock != NULL) {
+ /* Count resumes that don't belong to us. */
+ spurious_wakeup_count++;
+ continue;
+ }
+ break;
+ }
+ }
+
+ /* Put back any resumes we caught that don't belong to us. */
+ while (spurious_wakeup_count--)
+ restart(self);
}
void internal_function __pthread_unlock(struct _pthread_fastlock * lock)