aboutsummaryrefslogtreecommitdiff
path: root/nptl/DESIGN-condvar.txt
blob: cb0f59c823b260841c9b2bce514ec1f5521d6488 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
Conditional Variable pseudocode.
================================

       int pthread_cond_timedwait (pthread_cond_t *cv, pthread_mutex_t *mutex);
       int pthread_cond_signal    (pthread_cond_t *cv);
       int pthread_cond_broadcast (pthread_cond_t *cv);

struct pthread_cond_t {

   unsigned int cond_lock;

         internal mutex

   uint64_t total_seq;

     Total number of threads using the conditional variable.

   uint64_t wakeup_seq;

     sequence number for next wakeup.

   uint64_t woken_seq;

     sequence number of last woken thread.

}



cleanup_handler(cv)
{
  lll_lock(cv->lock);

  ++cv->wakeup_seq;
  ++cv->woken_seq;

  /* make sure no signal gets lost.  */
  FUTEX_WAKE(cv->wakeup_seq, ALL);

  lll_unlock(cv->lock);
}


cond_timedwait(cv, mutex, timeout):
{
   lll_lock(cv->lock);
   mutex_unlock(mutex);

   cleanup_push

   ++cv->total_seq;
   val = seq =  cv->wakeup_seq;

   while (1) {

     lll_unlock(cv->lock);

     enable_async

     ret = FUTEX_WAIT(cv->wakeup_seq, val, timeout);

     restore_async

     lll_lock(cv->lock);

     val = cv->wakeup_seq;

     if (val != seq && cv->woken_seq != val) {
       ret = 0;
       break;
     }

     if (ret == TIMEDOUT) {
       ++cv->wakeup_seq;
       break;
     }
   }

   ++cv->woken_seq;

   lll_unlock(cv->lock);

   cleanup_pop

   mutex_lock(mutex);

   return ret;
}

cond_signal(cv)
{
   lll_lock(cv->lock);

   if (cv->total_seq > cv->wakeup_seq) {
     ++cv->wakeup_seq;
     FUTEX_WAKE(cv->wakeup_seq, 1);
   }

   lll_unlock(cv->lock);
}

cond_broadcast(cv)
{
   lll_lock(cv->lock);

   if (cv->total_seq > cv->wakeup_seq) {
     cv->wakeup_seq = cv->total_seq;
     FUTEX_WAKE(cv->wakeup_seq, ALL);
   }

   lll_unlock(cv->lock);
}