Skip to content

Commit 947c346

Browse files
j6tgitster
authored andcommitted
Implement pthread_cond_broadcast on Windows
See http://www.cse.wustl.edu/~schmidt/win32-cv-1.html, section "The SignalObjectAndWait solution". But note that this implementation does not use SignalObjectAndWait (which is needed to achieve fairness, but we do not need fairness). Note that our implementations of pthread_cond_broadcast and pthread_cond_signal require that they are invoked with the mutex held that is used in the pthread_cond_wait calls. Signed-off-by: Johannes Sixt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a004fb9 commit 947c346

File tree

2 files changed

+94
-15
lines changed

2 files changed

+94
-15
lines changed

compat/win32/pthread.c

Lines changed: 89 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,24 +52,38 @@ int win32_pthread_join(pthread_t *thread, void **value_ptr)
5252
int pthread_cond_init(pthread_cond_t *cond, const void *unused)
5353
{
5454
cond->waiters = 0;
55+
cond->was_broadcast = 0;
56+
InitializeCriticalSection(&cond->waiters_lock);
5557

5658
cond->sema = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
5759
if (!cond->sema)
5860
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+
5969
return 0;
6070
}
6171

6272
int pthread_cond_destroy(pthread_cond_t *cond)
6373
{
6474
CloseHandle(cond->sema);
65-
cond->sema = NULL;
66-
75+
CloseHandle(cond->continue_broadcast);
76+
DeleteCriticalSection(&cond->waiters_lock);
6777
return 0;
6878
}
6979

7080
int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex)
7181
{
72-
InterlockedIncrement(&cond->waiters);
82+
int last_waiter;
83+
84+
EnterCriticalSection(&cond->waiters_lock);
85+
cond->waiters++;
86+
LeaveCriticalSection(&cond->waiters_lock);
7387

7488
/*
7589
* Unlock external mutex and wait for signal.
@@ -82,22 +96,52 @@ int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex)
8296
/* let's wait - ignore return value */
8397
WaitForSingleObject(cond->sema, INFINITE);
8498

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+
}
88127
/* lock external mutex again */
89128
EnterCriticalSection(mutex);
90129

91130
return 0;
92131
}
93132

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+
*/
94138
int pthread_cond_signal(pthread_cond_t *cond)
95139
{
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);
101145

102146
/*
103147
* Signal only when there are waiters
@@ -108,3 +152,37 @@ int pthread_cond_signal(pthread_cond_t *cond)
108152
else
109153
return 0;
110154
}
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+
}

compat/win32/pthread.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,18 @@
3232
* See also: http://www.cse.wustl.edu/~schmidt/win32-cv-1.html
3333
*/
3434
typedef struct {
35-
volatile LONG waiters;
35+
LONG waiters;
36+
int was_broadcast;
37+
CRITICAL_SECTION waiters_lock;
3638
HANDLE sema;
39+
HANDLE continue_broadcast;
3740
} pthread_cond_t;
3841

3942
extern int pthread_cond_init(pthread_cond_t *cond, const void *unused);
40-
4143
extern int pthread_cond_destroy(pthread_cond_t *cond);
42-
4344
extern int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex);
44-
4545
extern int pthread_cond_signal(pthread_cond_t *cond);
46+
extern int pthread_cond_broadcast(pthread_cond_t *cond);
4647

4748
/*
4849
* Simple thread creation implementation using pthread API

0 commit comments

Comments
 (0)