1 /* Condition variables (native Windows implementation).
2 Copyright (C) 2008-2020 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Yoann Vandoorselaere <yoann@prelude-ids.org>, 2008,
18 and Bruno Haible <bruno@clisp.org>, 2008. */
23 #include "windows-cond.h"
30 /* Don't assume that UNICODE is not defined. */
32 #define CreateEvent CreateEventA
34 /* In this file, the waitqueues are implemented as linked lists. */
35 #define glwthread_waitqueue_t glwthread_linked_waitqueue_t
37 /* All links of a circular list, except the anchor, are of this type, carrying
39 struct glwthread_waitqueue_element
41 struct glwthread_waitqueue_link link
; /* must be the first field! */
42 HANDLE event
; /* Waiting thread, represented by an event.
43 This field is immutable once initialized. */
47 glwthread_waitqueue_init (glwthread_waitqueue_t
*wq
)
49 wq
->wq_list
.wql_next
= &wq
->wq_list
;
50 wq
->wq_list
.wql_prev
= &wq
->wq_list
;
53 /* Enqueues the current thread, represented by an event, in a wait queue.
54 Returns NULL if an allocation failure occurs. */
55 static struct glwthread_waitqueue_element
*
56 glwthread_waitqueue_add (glwthread_waitqueue_t
*wq
)
58 struct glwthread_waitqueue_element
*elt
;
61 /* Allocate the memory for the waitqueue element on the heap, not on the
62 thread's stack. If the thread exits unexpectedly, we prefer to leak
63 some memory rather than to access unavailable memory and crash. */
65 (struct glwthread_waitqueue_element
*)
66 malloc (sizeof (struct glwthread_waitqueue_element
));
71 /* Whether the created event is a manual-reset one or an auto-reset one,
72 does not matter, since we will wait on it only once. */
73 event
= CreateEvent (NULL
, TRUE
, FALSE
, NULL
);
74 if (event
== INVALID_HANDLE_VALUE
)
76 /* No way to allocate an event. */
81 /* Insert elt at the end of the circular list. */
82 (elt
->link
.wql_prev
= wq
->wq_list
.wql_prev
)->wql_next
= &elt
->link
;
83 (elt
->link
.wql_next
= &wq
->wq_list
)->wql_prev
= &elt
->link
;
87 /* Removes the current thread, represented by a
88 'struct glwthread_waitqueue_element *', from a wait queue.
89 Returns true if is was found and removed, false if it was not present. */
91 glwthread_waitqueue_remove (glwthread_waitqueue_t
*wq
,
92 struct glwthread_waitqueue_element
*elt
)
94 if (elt
->link
.wql_next
!= NULL
&& elt
->link
.wql_prev
!= NULL
)
96 /* Remove elt from the circular list. */
97 struct glwthread_waitqueue_link
*prev
= elt
->link
.wql_prev
;
98 struct glwthread_waitqueue_link
*next
= elt
->link
.wql_next
;
99 prev
->wql_next
= next
;
100 next
->wql_prev
= prev
;
101 elt
->link
.wql_next
= NULL
;
102 elt
->link
.wql_prev
= NULL
;
109 /* Notifies the first thread from a wait queue and dequeues it. */
111 glwthread_waitqueue_notify_first (glwthread_waitqueue_t
*wq
)
113 if (wq
->wq_list
.wql_next
!= &wq
->wq_list
)
115 struct glwthread_waitqueue_element
*elt
=
116 (struct glwthread_waitqueue_element
*) wq
->wq_list
.wql_next
;
117 struct glwthread_waitqueue_link
*prev
;
118 struct glwthread_waitqueue_link
*next
;
120 /* Remove elt from the circular list. */
121 prev
= &wq
->wq_list
; /* = elt->link.wql_prev; */
122 next
= elt
->link
.wql_next
;
123 prev
->wql_next
= next
;
124 next
->wql_prev
= prev
;
125 elt
->link
.wql_next
= NULL
;
126 elt
->link
.wql_prev
= NULL
;
128 SetEvent (elt
->event
);
129 /* After the SetEvent, this thread cannot access *elt any more, because
130 the woken-up thread will quickly call free (elt). */
134 /* Notifies all threads from a wait queue and dequeues them all. */
136 glwthread_waitqueue_notify_all (glwthread_waitqueue_t
*wq
)
138 struct glwthread_waitqueue_link
*l
;
140 for (l
= wq
->wq_list
.wql_next
; l
!= &wq
->wq_list
; )
142 struct glwthread_waitqueue_element
*elt
=
143 (struct glwthread_waitqueue_element
*) l
;
144 struct glwthread_waitqueue_link
*prev
;
145 struct glwthread_waitqueue_link
*next
;
147 /* Remove elt from the circular list. */
148 prev
= &wq
->wq_list
; /* = elt->link.wql_prev; */
149 next
= elt
->link
.wql_next
;
150 prev
->wql_next
= next
;
151 next
->wql_prev
= prev
;
152 elt
->link
.wql_next
= NULL
;
153 elt
->link
.wql_prev
= NULL
;
155 SetEvent (elt
->event
);
156 /* After the SetEvent, this thread cannot access *elt any more, because
157 the woken-up thread will quickly call free (elt). */
161 if (!(wq
->wq_list
.wql_next
== &wq
->wq_list
162 && wq
->wq_list
.wql_prev
== &wq
->wq_list
))
167 glwthread_cond_init (glwthread_cond_t
*cond
)
169 InitializeCriticalSection (&cond
->lock
);
170 glwthread_waitqueue_init (&cond
->waiters
);
172 cond
->guard
.done
= 1;
177 glwthread_cond_wait (glwthread_cond_t
*cond
,
178 void *mutex
, int (*mutex_lock
) (void *), int (*mutex_unlock
) (void *))
180 if (!cond
->guard
.done
)
182 if (InterlockedIncrement (&cond
->guard
.started
) == 0)
183 /* This thread is the first one to need this condition variable.
185 glwthread_cond_init (cond
);
188 /* Don't let cond->guard.started grow and wrap around. */
189 InterlockedDecrement (&cond
->guard
.started
);
190 /* Yield the CPU while waiting for another thread to finish
191 initializing this condition variable. */
192 while (!cond
->guard
.done
)
197 EnterCriticalSection (&cond
->lock
);
199 struct glwthread_waitqueue_element
*elt
=
200 glwthread_waitqueue_add (&cond
->waiters
);
201 LeaveCriticalSection (&cond
->lock
);
204 /* Allocation failure. Weird. */
209 HANDLE event
= elt
->event
;
213 /* Now release the mutex and let any other thread take it. */
214 err
= mutex_unlock (mutex
);
217 EnterCriticalSection (&cond
->lock
);
218 glwthread_waitqueue_remove (&cond
->waiters
, elt
);
219 LeaveCriticalSection (&cond
->lock
);
225 "If another thread is able to acquire the mutex after the
226 about-to-block thread has released it, then a subsequent call to
227 pthread_cond_broadcast() or pthread_cond_signal() in that thread
228 shall behave as if it were issued after the about-to-block thread
230 This is fulfilled here, because the thread signalling is done
231 through SetEvent, not PulseEvent. */
232 /* Wait until another thread signals this event. */
233 result
= WaitForSingleObject (event
, INFINITE
);
234 if (result
== WAIT_FAILED
|| result
== WAIT_TIMEOUT
)
238 /* The thread which signalled the event already did the bookkeeping:
239 removed us from the waiters. */
240 return mutex_lock (mutex
);
246 glwthread_cond_timedwait (glwthread_cond_t
*cond
,
247 void *mutex
, int (*mutex_lock
) (void *), int (*mutex_unlock
) (void *),
248 const struct timespec
*abstime
)
250 if (!cond
->guard
.done
)
252 if (InterlockedIncrement (&cond
->guard
.started
) == 0)
253 /* This thread is the first one to need this condition variable.
255 glwthread_cond_init (cond
);
258 /* Don't let cond->guard.started grow and wrap around. */
259 InterlockedDecrement (&cond
->guard
.started
);
260 /* Yield the CPU while waiting for another thread to finish
261 initializing this condition variable. */
262 while (!cond
->guard
.done
)
268 struct timeval currtime
;
270 gettimeofday (&currtime
, NULL
);
271 if (currtime
.tv_sec
> abstime
->tv_sec
272 || (currtime
.tv_sec
== abstime
->tv_sec
273 && currtime
.tv_usec
* 1000 >= abstime
->tv_nsec
))
276 EnterCriticalSection (&cond
->lock
);
278 struct glwthread_waitqueue_element
*elt
=
279 glwthread_waitqueue_add (&cond
->waiters
);
280 LeaveCriticalSection (&cond
->lock
);
283 /* Allocation failure. Weird. */
288 HANDLE event
= elt
->event
;
293 /* Now release the mutex and let any other thread take it. */
294 err
= mutex_unlock (mutex
);
297 EnterCriticalSection (&cond
->lock
);
298 glwthread_waitqueue_remove (&cond
->waiters
, elt
);
299 LeaveCriticalSection (&cond
->lock
);
305 "If another thread is able to acquire the mutex after the
306 about-to-block thread has released it, then a subsequent call to
307 pthread_cond_broadcast() or pthread_cond_signal() in that thread
308 shall behave as if it were issued after the about-to-block thread
310 This is fulfilled here, because the thread signalling is done
311 through SetEvent, not PulseEvent. */
312 /* Wait until another thread signals this event or until the abstime
314 gettimeofday (&currtime
, NULL
);
315 if (currtime
.tv_sec
> abstime
->tv_sec
)
319 unsigned long seconds
= abstime
->tv_sec
- currtime
.tv_sec
;
320 timeout
= seconds
* 1000;
321 if (timeout
/ 1000 != seconds
) /* overflow? */
326 abstime
->tv_nsec
/ 1000000 - currtime
.tv_usec
/ 1000;
327 if (milliseconds
>= 0)
329 timeout
+= milliseconds
;
330 if (timeout
< milliseconds
) /* overflow? */
335 if (timeout
>= - milliseconds
)
336 timeout
-= (- milliseconds
);
342 result
= WaitForSingleObject (event
, timeout
);
343 if (result
== WAIT_FAILED
)
345 if (result
== WAIT_TIMEOUT
)
347 EnterCriticalSection (&cond
->lock
);
348 if (glwthread_waitqueue_remove (&cond
->waiters
, elt
))
350 /* The event was not signaled between the WaitForSingleObject
351 call and the EnterCriticalSection call. */
352 if (!(WaitForSingleObject (event
, 0) == WAIT_TIMEOUT
))
357 /* The event was signaled between the WaitForSingleObject
358 call and the EnterCriticalSection call. */
359 if (!(WaitForSingleObject (event
, 0) == WAIT_OBJECT_0
))
361 /* Produce the right return value. */
362 result
= WAIT_OBJECT_0
;
364 LeaveCriticalSection (&cond
->lock
);
368 /* The thread which signalled the event already did the
369 bookkeeping: removed us from the waiters. */
373 /* Take the mutex again. It does not matter whether this is done
374 before or after the bookkeeping for WAIT_TIMEOUT. */
375 err
= mutex_lock (mutex
);
377 result
== WAIT_OBJECT_0
? 0 :
378 result
== WAIT_TIMEOUT
? ETIMEDOUT
:
379 /* WAIT_FAILED shouldn't happen */ EAGAIN
);
386 glwthread_cond_signal (glwthread_cond_t
*cond
)
388 if (!cond
->guard
.done
)
391 EnterCriticalSection (&cond
->lock
);
393 "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
394 have no effect if there are no threads currently blocked on cond." */
395 if (cond
->waiters
.wq_list
.wql_next
!= &cond
->waiters
.wq_list
)
396 glwthread_waitqueue_notify_first (&cond
->waiters
);
397 LeaveCriticalSection (&cond
->lock
);
403 glwthread_cond_broadcast (glwthread_cond_t
*cond
)
405 if (!cond
->guard
.done
)
408 EnterCriticalSection (&cond
->lock
);
410 "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
411 have no effect if there are no threads currently blocked on cond."
412 glwthread_waitqueue_notify_all is a nop in this case. */
413 glwthread_waitqueue_notify_all (&cond
->waiters
);
414 LeaveCriticalSection (&cond
->lock
);
420 glwthread_cond_destroy (glwthread_cond_t
*cond
)
422 if (!cond
->guard
.done
)
424 if (cond
->waiters
.wq_list
.wql_next
!= &cond
->waiters
.wq_list
)
426 DeleteCriticalSection (&cond
->lock
);
427 cond
->guard
.done
= 0;