1 /* Condition variables (native Windows implementation).
2 Copyright (C) 2008-2024 Free Software Foundation, Inc.
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
9 This file 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 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser 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"
29 /* Don't assume that UNICODE is not defined. */
31 #define CreateEvent CreateEventA
33 /* In this file, the waitqueues are implemented as linked lists. */
34 #define glwthread_waitqueue_t glwthread_linked_waitqueue_t
36 /* All links of a circular list, except the anchor, are of this type, carrying
38 struct glwthread_waitqueue_element
40 struct glwthread_waitqueue_link link
; /* must be the first field! */
41 HANDLE event
; /* Waiting thread, represented by an event.
42 This field is immutable once initialized. */
46 glwthread_waitqueue_init (glwthread_waitqueue_t
*wq
)
48 wq
->wq_list
.wql_next
= &wq
->wq_list
;
49 wq
->wq_list
.wql_prev
= &wq
->wq_list
;
52 /* Enqueues the current thread, represented by an event, in a wait queue.
53 Returns NULL if an allocation failure occurs. */
54 static struct glwthread_waitqueue_element
*
55 glwthread_waitqueue_add (glwthread_waitqueue_t
*wq
)
57 struct glwthread_waitqueue_element
*elt
;
60 /* Allocate the memory for the waitqueue element on the heap, not on the
61 thread's stack. If the thread exits unexpectedly, we prefer to leak
62 some memory rather than to access unavailable memory and crash. */
64 (struct glwthread_waitqueue_element
*)
65 malloc (sizeof (struct glwthread_waitqueue_element
));
70 /* Whether the created event is a manual-reset one or an auto-reset one,
71 does not matter, since we will wait on it only once. */
72 event
= CreateEvent (NULL
, TRUE
, FALSE
, NULL
);
73 if (event
== INVALID_HANDLE_VALUE
)
75 /* No way to allocate an event. */
80 /* Insert elt at the end of the circular list. */
81 (elt
->link
.wql_prev
= wq
->wq_list
.wql_prev
)->wql_next
= &elt
->link
;
82 (elt
->link
.wql_next
= &wq
->wq_list
)->wql_prev
= &elt
->link
;
86 /* Removes the current thread, represented by a
87 'struct glwthread_waitqueue_element *', from a wait queue.
88 Returns true if is was found and removed, false if it was not present. */
90 glwthread_waitqueue_remove (glwthread_waitqueue_t
*wq
,
91 struct glwthread_waitqueue_element
*elt
)
93 if (elt
->link
.wql_next
!= NULL
&& elt
->link
.wql_prev
!= NULL
)
95 /* Remove elt from the circular list. */
96 struct glwthread_waitqueue_link
*prev
= elt
->link
.wql_prev
;
97 struct glwthread_waitqueue_link
*next
= elt
->link
.wql_next
;
98 prev
->wql_next
= next
;
99 next
->wql_prev
= prev
;
100 elt
->link
.wql_next
= NULL
;
101 elt
->link
.wql_prev
= NULL
;
108 /* Notifies the first thread from a wait queue and dequeues it. */
110 glwthread_waitqueue_notify_first (glwthread_waitqueue_t
*wq
)
112 if (wq
->wq_list
.wql_next
!= &wq
->wq_list
)
114 struct glwthread_waitqueue_element
*elt
=
115 (struct glwthread_waitqueue_element
*) wq
->wq_list
.wql_next
;
116 struct glwthread_waitqueue_link
*prev
;
117 struct glwthread_waitqueue_link
*next
;
119 /* Remove elt from the circular list. */
120 prev
= &wq
->wq_list
; /* = elt->link.wql_prev; */
121 next
= elt
->link
.wql_next
;
122 prev
->wql_next
= next
;
123 next
->wql_prev
= prev
;
124 elt
->link
.wql_next
= NULL
;
125 elt
->link
.wql_prev
= NULL
;
127 SetEvent (elt
->event
);
128 /* After the SetEvent, this thread cannot access *elt any more, because
129 the woken-up thread will quickly call free (elt). */
133 /* Notifies all threads from a wait queue and dequeues them all. */
135 glwthread_waitqueue_notify_all (glwthread_waitqueue_t
*wq
)
137 struct glwthread_waitqueue_link
*l
;
139 for (l
= wq
->wq_list
.wql_next
; l
!= &wq
->wq_list
; )
141 struct glwthread_waitqueue_element
*elt
=
142 (struct glwthread_waitqueue_element
*) l
;
143 struct glwthread_waitqueue_link
*prev
;
144 struct glwthread_waitqueue_link
*next
;
146 /* Remove elt from the circular list. */
147 prev
= &wq
->wq_list
; /* = elt->link.wql_prev; */
148 next
= elt
->link
.wql_next
;
149 prev
->wql_next
= next
;
150 next
->wql_prev
= prev
;
151 elt
->link
.wql_next
= NULL
;
152 elt
->link
.wql_prev
= NULL
;
154 SetEvent (elt
->event
);
155 /* After the SetEvent, this thread cannot access *elt any more, because
156 the woken-up thread will quickly call free (elt). */
160 if (!(wq
->wq_list
.wql_next
== &wq
->wq_list
161 && wq
->wq_list
.wql_prev
== &wq
->wq_list
))
166 glwthread_cond_init (glwthread_cond_t
*cond
)
168 InitializeCriticalSection (&cond
->lock
);
169 glwthread_waitqueue_init (&cond
->waiters
);
171 cond
->guard
.done
= 1;
176 glwthread_cond_wait (glwthread_cond_t
*cond
,
177 void *mutex
, int (*mutex_lock
) (void *), int (*mutex_unlock
) (void *))
179 if (!cond
->guard
.done
)
181 if (InterlockedIncrement (&cond
->guard
.started
) == 0)
182 /* This thread is the first one to need this condition variable.
184 glwthread_cond_init (cond
);
187 /* Don't let cond->guard.started grow and wrap around. */
188 InterlockedDecrement (&cond
->guard
.started
);
189 /* Yield the CPU while waiting for another thread to finish
190 initializing this condition variable. */
191 while (!cond
->guard
.done
)
196 EnterCriticalSection (&cond
->lock
);
198 struct glwthread_waitqueue_element
*elt
=
199 glwthread_waitqueue_add (&cond
->waiters
);
200 LeaveCriticalSection (&cond
->lock
);
203 /* Allocation failure. Weird. */
208 HANDLE event
= elt
->event
;
212 /* Now release the mutex and let any other thread take it. */
213 err
= mutex_unlock (mutex
);
216 EnterCriticalSection (&cond
->lock
);
217 glwthread_waitqueue_remove (&cond
->waiters
, elt
);
218 LeaveCriticalSection (&cond
->lock
);
224 "If another thread is able to acquire the mutex after the
225 about-to-block thread has released it, then a subsequent call to
226 pthread_cond_broadcast() or pthread_cond_signal() in that thread
227 shall behave as if it were issued after the about-to-block thread
229 This is fulfilled here, because the thread signalling is done
230 through SetEvent, not PulseEvent. */
231 /* Wait until another thread signals this event. */
232 result
= WaitForSingleObject (event
, INFINITE
);
233 if (result
== WAIT_FAILED
|| result
== WAIT_TIMEOUT
)
237 /* The thread which signalled the event already did the bookkeeping:
238 removed us from the waiters. */
239 return mutex_lock (mutex
);
245 glwthread_cond_timedwait (glwthread_cond_t
*cond
,
246 void *mutex
, int (*mutex_lock
) (void *), int (*mutex_unlock
) (void *),
247 const struct timespec
*abstime
)
249 if (!cond
->guard
.done
)
251 if (InterlockedIncrement (&cond
->guard
.started
) == 0)
252 /* This thread is the first one to need this condition variable.
254 glwthread_cond_init (cond
);
257 /* Don't let cond->guard.started grow and wrap around. */
258 InterlockedDecrement (&cond
->guard
.started
);
259 /* Yield the CPU while waiting for another thread to finish
260 initializing this condition variable. */
261 while (!cond
->guard
.done
)
267 struct timeval currtime
;
269 gettimeofday (&currtime
, NULL
);
270 if (currtime
.tv_sec
> abstime
->tv_sec
271 || (currtime
.tv_sec
== abstime
->tv_sec
272 && currtime
.tv_usec
* 1000 >= abstime
->tv_nsec
))
275 EnterCriticalSection (&cond
->lock
);
277 struct glwthread_waitqueue_element
*elt
=
278 glwthread_waitqueue_add (&cond
->waiters
);
279 LeaveCriticalSection (&cond
->lock
);
282 /* Allocation failure. Weird. */
287 HANDLE event
= elt
->event
;
292 /* Now release the mutex and let any other thread take it. */
293 err
= mutex_unlock (mutex
);
296 EnterCriticalSection (&cond
->lock
);
297 glwthread_waitqueue_remove (&cond
->waiters
, elt
);
298 LeaveCriticalSection (&cond
->lock
);
304 "If another thread is able to acquire the mutex after the
305 about-to-block thread has released it, then a subsequent call to
306 pthread_cond_broadcast() or pthread_cond_signal() in that thread
307 shall behave as if it were issued after the about-to-block thread
309 This is fulfilled here, because the thread signalling is done
310 through SetEvent, not PulseEvent. */
311 /* Wait until another thread signals this event or until the abstime
313 gettimeofday (&currtime
, NULL
);
314 if (currtime
.tv_sec
> abstime
->tv_sec
)
318 unsigned long seconds
= abstime
->tv_sec
- currtime
.tv_sec
;
319 timeout
= seconds
* 1000;
320 if (timeout
/ 1000 != seconds
) /* overflow? */
325 abstime
->tv_nsec
/ 1000000 - currtime
.tv_usec
/ 1000;
326 if (milliseconds
>= 0)
328 timeout
+= milliseconds
;
329 if (timeout
< milliseconds
) /* overflow? */
334 if (timeout
>= - milliseconds
)
335 timeout
-= (- milliseconds
);
341 result
= WaitForSingleObject (event
, timeout
);
342 if (result
== WAIT_FAILED
)
344 if (result
== WAIT_TIMEOUT
)
346 EnterCriticalSection (&cond
->lock
);
347 if (glwthread_waitqueue_remove (&cond
->waiters
, elt
))
349 /* The event was not signaled between the WaitForSingleObject
350 call and the EnterCriticalSection call. */
351 if (!(WaitForSingleObject (event
, 0) == WAIT_TIMEOUT
))
356 /* The event was signaled between the WaitForSingleObject
357 call and the EnterCriticalSection call. */
358 if (!(WaitForSingleObject (event
, 0) == WAIT_OBJECT_0
))
360 /* Produce the right return value. */
361 result
= WAIT_OBJECT_0
;
363 LeaveCriticalSection (&cond
->lock
);
367 /* The thread which signalled the event already did the
368 bookkeeping: removed us from the waiters. */
372 /* Take the mutex again. It does not matter whether this is done
373 before or after the bookkeeping for WAIT_TIMEOUT. */
374 err
= mutex_lock (mutex
);
376 result
== WAIT_OBJECT_0
? 0 :
377 result
== WAIT_TIMEOUT
? ETIMEDOUT
:
378 /* WAIT_FAILED shouldn't happen */ EAGAIN
);
385 glwthread_cond_signal (glwthread_cond_t
*cond
)
387 if (!cond
->guard
.done
)
390 EnterCriticalSection (&cond
->lock
);
392 "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
393 have no effect if there are no threads currently blocked on cond." */
394 if (cond
->waiters
.wq_list
.wql_next
!= &cond
->waiters
.wq_list
)
395 glwthread_waitqueue_notify_first (&cond
->waiters
);
396 LeaveCriticalSection (&cond
->lock
);
402 glwthread_cond_broadcast (glwthread_cond_t
*cond
)
404 if (!cond
->guard
.done
)
407 EnterCriticalSection (&cond
->lock
);
409 "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
410 have no effect if there are no threads currently blocked on cond."
411 glwthread_waitqueue_notify_all is a nop in this case. */
412 glwthread_waitqueue_notify_all (&cond
->waiters
);
413 LeaveCriticalSection (&cond
->lock
);
419 glwthread_cond_destroy (glwthread_cond_t
*cond
)
421 if (!cond
->guard
.done
)
423 if (cond
->waiters
.wq_list
.wql_next
!= &cond
->waiters
.wq_list
)
425 DeleteCriticalSection (&cond
->lock
);
426 cond
->guard
.done
= 0;