about summary refs log tree commit diff
path: root/nptl/DESIGN-condvar.txt
diff options
context:
space:
mode:
Diffstat (limited to 'nptl/DESIGN-condvar.txt')
-rw-r--r--nptl/DESIGN-condvar.txt101
1 files changed, 60 insertions, 41 deletions
diff --git a/nptl/DESIGN-condvar.txt b/nptl/DESIGN-condvar.txt
index 303807be6d..749180ed4b 100644
--- a/nptl/DESIGN-condvar.txt
+++ b/nptl/DESIGN-condvar.txt
@@ -7,49 +7,78 @@ Conditional Variable pseudocode.
 
 struct pthread_cond_t {
 
-   unsigned int lock:
+   unsigned int cond_lock;
 
          internal mutex
 
-   unsigned int nr_wakers:
+   uint64_t total_seq;
 
-         number of threads signalled to be woken up.
+     Total number of threads using the conditional variable.
 
-   unsigned int nr_sleepers:
+   uint64_t wakeup_seq;
 
-         number of threads waiting for the cv.
+     sequence number for next wakeup.
+
+   uint64_t woken_seq;
+
+     sequence number of last woken thread.
 
 }
 
-#define ALL_THREADS (1 << (BITS_PER_LONG-1))
 
-cond_wait_timeout(cv, mutex, timeout):
+
+cleanup_handler(cv)
+{
+  lll_lock(cv->lock);
+
+  ++cv->wakeup_seq;
+  ++cv->woken_seq;
+
+  lll_unlock(cv->lock);
+}
+
+
+cond_timedwait(cv, mutex, timeout):
 {
    lll_lock(cv->lock);
    mutex_unlock(mutex);
 
-   cv->nr_sleepers++;
-   for (;;) {
+   cleanup_push
+
+   ++cv->total_seq;
+   val = seq =  cv->wakeup_seq;
+
+   while (1) {
+
+     lll_unlock(cv->lock);
+
+     enable_async
 
-       if (cv->nr_wakers) {
-           cv->nr_wakers--;
-           break;
-       }
-       val = cv->nr_wakers;
+     ret = FUTEX_WAIT(cv->wakeup_seq, val, timeout);
 
-       lll_unlock(cv->lock);
+     restore_async
 
-       ret = FUTEX WAIT (cv->nr_wakers, val, timeout)
+     lll_lock(cv->lock);
 
-       lll_lock(cv->lock);
+     val = cv->wakeup_seq;
 
-       if (ret == TIMEOUT)
-         break;
+     if (cv->woken_seq >= seq && cv->woken_seq < val) {
        ret = 0;
+       break;
+     }
+
+     if (ret == TIMEDOUT) {
+       ++cv->wakeup_seq;
+       break;
+     }
    }
-   if (!--cv->nr_sleepers)
-     cv->nr_wakers = 0; /* no memory of wakeups */
+
+   ++cv->woken_seq;
+
    lll_unlock(cv->lock);
+
+   cleanup_pop
+
    mutex_lock(mutex);
 
    return ret;
@@ -57,34 +86,24 @@ cond_wait_timeout(cv, mutex, timeout):
 
 cond_signal(cv)
 {
-   int do_wakeup = 0;
-
    lll_lock(cv->lock);
-   if (cv->nr_sleepers) {
-     if (!++cv->nr_wakers) /* overflow detection for the nutcase */
-       cv->nr_wakers = ALL_THREADS;
-     do_wakeup = 1;
+
+   if (cv->total_seq > cv->wakeup_seq) {
+     ++cv->wakeup_seq;
+     FUTEX_WAKE(cv->wakeup_seq, 1);
    }
+
    lll_unlock(cv->lock);
-   if (do_wakeup)
-     FUTEX WAKE (cv->nr_wakers, 1)
 }
 
 cond_broadcast(cv)
 {
-   int do_wakeup = 0;
-
    lll_lock(cv->lock);
-   if (cv->nr_sleepers) {
-     cv->nr_wakers |= ALL_THREADS;
-     do_wakeup = 1;
+
+   if (cv->total_seq > cv->wakeup_seq) {
+     cv->wakeup_seq = cv->total_seq;
+     FUTEX_WAKE(cv->wakeup_seq, ALL);
    }
+
    lll_unlock(cv->lock);
-   if (do_wakeup)
-     FUTEX WAKE (cv->nr_wakers, ALL_THREADS);
 }
-
-weaknesses of the implementation:
-
- it might generate spurious wakeups in the broadcast case, but those are
- allowed by POSIX.