havelib: Fix for Solaris 11 OpenIndiana and Solaris 11 OmniOS.
[gnulib.git] / lib / windows-cond.c
blob45e9b836ca9c23061f273b7916b75b948c971d64
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)
7 any later version.
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. */
20 #include <config.h>
22 /* Specification. */
23 #include "windows-cond.h"
25 #include <errno.h>
26 #include <stdbool.h>
27 #include <stdlib.h>
28 #include <sys/time.h>
30 /* Don't assume that UNICODE is not defined. */
31 #undef CreateEvent
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
38 a payload. */
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. */
46 static void
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;
59 HANDLE event;
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. */
64 elt =
65 (struct glwthread_waitqueue_element *)
66 malloc (sizeof (struct glwthread_waitqueue_element));
67 if (elt == NULL)
68 /* No more memory. */
69 return NULL;
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. */
77 free (elt);
78 return NULL;
80 elt->event = 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;
84 return elt;
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. */
90 static bool
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;
103 return true;
105 else
106 return false;
109 /* Notifies the first thread from a wait queue and dequeues it. */
110 static void
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. */
135 static void
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). */
159 l = next;
161 if (!(wq->wq_list.wql_next == &wq->wq_list
162 && wq->wq_list.wql_prev == &wq->wq_list))
163 abort ();
167 glwthread_cond_init (glwthread_cond_t *cond)
169 InitializeCriticalSection (&cond->lock);
170 glwthread_waitqueue_init (&cond->waiters);
172 cond->guard.done = 1;
173 return 0;
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.
184 Initialize it. */
185 glwthread_cond_init (cond);
186 else
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)
193 Sleep (0);
197 EnterCriticalSection (&cond->lock);
199 struct glwthread_waitqueue_element *elt =
200 glwthread_waitqueue_add (&cond->waiters);
201 LeaveCriticalSection (&cond->lock);
202 if (elt == NULL)
204 /* Allocation failure. Weird. */
205 return EAGAIN;
207 else
209 HANDLE event = elt->event;
210 int err;
211 DWORD result;
213 /* Now release the mutex and let any other thread take it. */
214 err = mutex_unlock (mutex);
215 if (err != 0)
217 EnterCriticalSection (&cond->lock);
218 glwthread_waitqueue_remove (&cond->waiters, elt);
219 LeaveCriticalSection (&cond->lock);
220 CloseHandle (event);
221 free (elt);
222 return err;
224 /* POSIX says:
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
229 has blocked."
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)
235 abort ();
236 CloseHandle (event);
237 free (elt);
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.
254 Initialize it. */
255 glwthread_cond_init (cond);
256 else
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)
263 Sleep (0);
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))
274 return ETIMEDOUT;
276 EnterCriticalSection (&cond->lock);
278 struct glwthread_waitqueue_element *elt =
279 glwthread_waitqueue_add (&cond->waiters);
280 LeaveCriticalSection (&cond->lock);
281 if (elt == NULL)
283 /* Allocation failure. Weird. */
284 return EAGAIN;
286 else
288 HANDLE event = elt->event;
289 int err;
290 DWORD timeout;
291 DWORD result;
293 /* Now release the mutex and let any other thread take it. */
294 err = mutex_unlock (mutex);
295 if (err != 0)
297 EnterCriticalSection (&cond->lock);
298 glwthread_waitqueue_remove (&cond->waiters, elt);
299 LeaveCriticalSection (&cond->lock);
300 CloseHandle (event);
301 free (elt);
302 return err;
304 /* POSIX says:
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
309 has blocked."
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
313 passes. */
314 gettimeofday (&currtime, NULL);
315 if (currtime.tv_sec > abstime->tv_sec)
316 timeout = 0;
317 else
319 unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
320 timeout = seconds * 1000;
321 if (timeout / 1000 != seconds) /* overflow? */
322 timeout = INFINITE;
323 else
325 long milliseconds =
326 abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
327 if (milliseconds >= 0)
329 timeout += milliseconds;
330 if (timeout < milliseconds) /* overflow? */
331 timeout = INFINITE;
333 else
335 if (timeout >= - milliseconds)
336 timeout -= (- milliseconds);
337 else
338 timeout = 0;
342 result = WaitForSingleObject (event, timeout);
343 if (result == WAIT_FAILED)
344 abort ();
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))
353 abort ();
355 else
357 /* The event was signaled between the WaitForSingleObject
358 call and the EnterCriticalSection call. */
359 if (!(WaitForSingleObject (event, 0) == WAIT_OBJECT_0))
360 abort ();
361 /* Produce the right return value. */
362 result = WAIT_OBJECT_0;
364 LeaveCriticalSection (&cond->lock);
366 else
368 /* The thread which signalled the event already did the
369 bookkeeping: removed us from the waiters. */
371 CloseHandle (event);
372 free (elt);
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);
376 return (err ? err :
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)
389 return EINVAL;
391 EnterCriticalSection (&cond->lock);
392 /* POSIX says:
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);
399 return 0;
403 glwthread_cond_broadcast (glwthread_cond_t *cond)
405 if (!cond->guard.done)
406 return EINVAL;
408 EnterCriticalSection (&cond->lock);
409 /* POSIX says:
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);
416 return 0;
420 glwthread_cond_destroy (glwthread_cond_t *cond)
422 if (!cond->guard.done)
423 return EINVAL;
424 if (cond->waiters.wq_list.wql_next != &cond->waiters.wq_list)
425 return EBUSY;
426 DeleteCriticalSection (&cond->lock);
427 cond->guard.done = 0;
428 return 0;