@@ -52,24 +52,38 @@ int win32_pthread_join(pthread_t *thread, void **value_ptr)
52
52
int pthread_cond_init (pthread_cond_t * cond , const void * unused )
53
53
{
54
54
cond -> waiters = 0 ;
55
+ cond -> was_broadcast = 0 ;
56
+ InitializeCriticalSection (& cond -> waiters_lock );
55
57
56
58
cond -> sema = CreateSemaphore (NULL , 0 , LONG_MAX , NULL );
57
59
if (!cond -> sema )
58
60
die ("CreateSemaphore() failed" );
61
+
62
+ cond -> continue_broadcast = CreateEvent (NULL , /* security */
63
+ FALSE, /* auto-reset */
64
+ FALSE, /* not signaled */
65
+ NULL ); /* name */
66
+ if (!cond -> continue_broadcast )
67
+ die ("CreateEvent() failed" );
68
+
59
69
return 0 ;
60
70
}
61
71
62
72
int pthread_cond_destroy (pthread_cond_t * cond )
63
73
{
64
74
CloseHandle (cond -> sema );
65
- cond -> sema = NULL ;
66
-
75
+ CloseHandle ( cond -> continue_broadcast ) ;
76
+ DeleteCriticalSection ( & cond -> waiters_lock );
67
77
return 0 ;
68
78
}
69
79
70
80
int pthread_cond_wait (pthread_cond_t * cond , CRITICAL_SECTION * mutex )
71
81
{
72
- InterlockedIncrement (& cond -> waiters );
82
+ int last_waiter ;
83
+
84
+ EnterCriticalSection (& cond -> waiters_lock );
85
+ cond -> waiters ++ ;
86
+ LeaveCriticalSection (& cond -> waiters_lock );
73
87
74
88
/*
75
89
* Unlock external mutex and wait for signal.
@@ -82,22 +96,52 @@ int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex)
82
96
/* let's wait - ignore return value */
83
97
WaitForSingleObject (cond -> sema , INFINITE );
84
98
85
- /* we're done waiting, so make sure we decrease waiters count */
86
- InterlockedDecrement (& cond -> waiters );
87
-
99
+ /*
100
+ * Decrease waiters count. If we are the last waiter, then we must
101
+ * notify the broadcasting thread that it can continue.
102
+ * But if we continued due to cond_signal, we do not have to do that
103
+ * because the signaling thread knows that only one waiter continued.
104
+ */
105
+ EnterCriticalSection (& cond -> waiters_lock );
106
+ cond -> waiters -- ;
107
+ last_waiter = cond -> was_broadcast && cond -> waiters == 0 ;
108
+ LeaveCriticalSection (& cond -> waiters_lock );
109
+
110
+ if (last_waiter ) {
111
+ /*
112
+ * cond_broadcast was issued while mutex was held. This means
113
+ * that all other waiters have continued, but are contending
114
+ * for the mutex at the end of this function because the
115
+ * broadcasting thread did not leave cond_broadcast, yet.
116
+ * (This is so that it can be sure that each waiter has
117
+ * consumed exactly one slice of the semaphor.)
118
+ * The last waiter must tell the broadcasting thread that it
119
+ * can go on.
120
+ */
121
+ SetEvent (cond -> continue_broadcast );
122
+ /*
123
+ * Now we go on to contend with all other waiters for
124
+ * the mutex. Auf in den Kampf!
125
+ */
126
+ }
88
127
/* lock external mutex again */
89
128
EnterCriticalSection (mutex );
90
129
91
130
return 0 ;
92
131
}
93
132
133
+ /*
134
+ * IMPORTANT: This implementation requires that pthread_cond_signal
135
+ * is called while the mutex is held that is used in the corresponding
136
+ * pthread_cond_wait calls!
137
+ */
94
138
int pthread_cond_signal (pthread_cond_t * cond )
95
139
{
96
- /*
97
- * Access to waiters count is atomic; see "Interlocked Variable Access"
98
- * http://msdn.microsoft.com/en-us/library/ms684122(VS.85).aspx
99
- */
100
- int have_waiters = cond -> waiters > 0 ;
140
+ int have_waiters ;
141
+
142
+ EnterCriticalSection ( & cond -> waiters_lock );
143
+ have_waiters = cond -> waiters > 0 ;
144
+ LeaveCriticalSection ( & cond -> waiters_lock ) ;
101
145
102
146
/*
103
147
* Signal only when there are waiters
@@ -108,3 +152,37 @@ int pthread_cond_signal(pthread_cond_t *cond)
108
152
else
109
153
return 0 ;
110
154
}
155
+
156
+ /*
157
+ * DOUBLY IMPORTANT: This implementation requires that pthread_cond_broadcast
158
+ * is called while the mutex is held that is used in the corresponding
159
+ * pthread_cond_wait calls!
160
+ */
161
+ int pthread_cond_broadcast (pthread_cond_t * cond )
162
+ {
163
+ EnterCriticalSection (& cond -> waiters_lock );
164
+
165
+ if ((cond -> was_broadcast = cond -> waiters > 0 )) {
166
+ /* wake up all waiters */
167
+ ReleaseSemaphore (cond -> sema , cond -> waiters , NULL );
168
+ LeaveCriticalSection (& cond -> waiters_lock );
169
+ /*
170
+ * At this point all waiters continue. Each one takes its
171
+ * slice of the semaphor. Now it's our turn to wait: Since
172
+ * the external mutex is held, no thread can leave cond_wait,
173
+ * yet. For this reason, we can be sure that no thread gets
174
+ * a chance to eat *more* than one slice. OTOH, it means
175
+ * that the last waiter must send us a wake-up.
176
+ */
177
+ WaitForSingleObject (cond -> continue_broadcast , INFINITE );
178
+ /*
179
+ * Since the external mutex is held, no thread can enter
180
+ * cond_wait, and, hence, it is safe to reset this flag
181
+ * without cond->waiters_lock held.
182
+ */
183
+ cond -> was_broadcast = 0 ;
184
+ } else {
185
+ LeaveCriticalSection (& cond -> waiters_lock );
186
+ }
187
+ return 0 ;
188
+ }
0 commit comments