about summary refs log tree commit diff
path: root/linuxthreads/semaphore.c
diff options
context:
space:
mode:
Diffstat (limited to 'linuxthreads/semaphore.c')
-rw-r--r--linuxthreads/semaphore.c62
1 files changed, 48 insertions, 14 deletions
diff --git a/linuxthreads/semaphore.c b/linuxthreads/semaphore.c
index a772ea5091..e5afce43eb 100644
--- a/linuxthreads/semaphore.c
+++ b/linuxthreads/semaphore.c
@@ -60,6 +60,7 @@ int __new_sem_wait(sem_t * sem)
   volatile pthread_descr self = thread_self();
   pthread_extricate_if extr;
   int already_canceled = 0;
+  int spurious_wakeup_count;
 
   /* Set up extrication interface */
   extr.pu_object = sem;
@@ -72,6 +73,7 @@ int __new_sem_wait(sem_t * sem)
     return 0;
   }
   /* Register extrication interface */
+  THREAD_SETMEM(self, p_sem_avail, 0);
   __pthread_set_own_extricate_if(self, &extr);
   /* Enqueue only if not already cancelled. */
   if (!(THREAD_GETMEM(self, p_canceled)
@@ -87,7 +89,20 @@ int __new_sem_wait(sem_t * sem)
   }
 
   /* Wait for sem_post or cancellation, or fall through if already canceled */
-  suspend(self);
+  spurious_wakeup_count = 0;
+  while (1)
+    {
+      suspend(self);
+      if (THREAD_GETMEM(self, p_sem_avail) == 0
+	  && (THREAD_GETMEM(self, p_woken_by_cancel) == 0
+	      || THREAD_GETMEM(self, p_cancelstate) != PTHREAD_CANCEL_ENABLE))
+	{
+	  /* Count resumes that don't belong to us. */
+	  spurious_wakeup_count++;
+	  continue;
+	}
+      break;
+    }
   __pthread_set_own_extricate_if(self, 0);
 
   /* Terminate only if the wakeup came from cancellation. */
@@ -138,6 +153,8 @@ int __new_sem_post(sem_t * sem)
     } else {
       th = dequeue(&sem->__sem_waiting);
       __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
+      th->p_sem_avail = 1;
+      WRITE_MEMORY_BARRIER();
       restart(th);
     }
   } else {
@@ -195,6 +212,7 @@ int sem_timedwait(sem_t *sem, const struct timespec *abstime)
   pthread_descr self = thread_self();
   pthread_extricate_if extr;
   int already_canceled = 0;
+  int spurious_wakeup_count;
 
   __pthread_lock((struct _pthread_fastlock *) &sem->__sem_lock, self);
   if (sem->__sem_value > 0) {
@@ -215,6 +233,7 @@ int sem_timedwait(sem_t *sem, const struct timespec *abstime)
   extr.pu_extricate_func = new_sem_extricate_func;
 
   /* Register extrication interface */
+  THREAD_SETMEM(self, p_sem_avail, 0);
   __pthread_set_own_extricate_if(self, &extr);
   /* Enqueue only if not already cancelled. */
   if (!(THREAD_GETMEM(self, p_canceled)
@@ -229,24 +248,39 @@ int sem_timedwait(sem_t *sem, const struct timespec *abstime)
     pthread_exit(PTHREAD_CANCELED);
   }
 
-  if (timedsuspend(self, abstime) == 0) {
-    int was_on_queue;
+  spurious_wakeup_count = 0;
+  while (1)
+    {
+      if (timedsuspend(self, abstime) == 0) {
+	int was_on_queue;
+
+	/* __pthread_lock will queue back any spurious restarts that
+	   may happen to it. */
 
-    /* __pthread_lock will queue back any spurious restarts that
-       may happen to it. */
+	__pthread_lock((struct _pthread_fastlock *)&sem->__sem_lock, self);
+	was_on_queue = remove_from_queue(&sem->__sem_waiting, self);
+	__pthread_unlock((struct _pthread_fastlock *)&sem->__sem_lock);
 
-    __pthread_lock((struct _pthread_fastlock *)&sem->__sem_lock, self);
-    was_on_queue = remove_from_queue(&sem->__sem_waiting, self);
-    __pthread_unlock((struct _pthread_fastlock *)&sem->__sem_lock);
+	if (was_on_queue) {
+	  __pthread_set_own_extricate_if(self, 0);
+	  return ETIMEDOUT;
+	}
 
-    if (was_on_queue) {
-      __pthread_set_own_extricate_if(self, 0);
-      return ETIMEDOUT;
+	/* Eat the outstanding restart() from the signaller */
+	suspend(self);
+      }
+
+      if (THREAD_GETMEM(self, p_sem_avail) == 0
+	  && (THREAD_GETMEM(self, p_woken_by_cancel) == 0
+	      || THREAD_GETMEM(self, p_cancelstate) != PTHREAD_CANCEL_ENABLE))
+	{
+	  /* 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);
 
   /* Terminate only if the wakeup came from cancellation. */