1 /* Condition variables for multithreading.
2 Copyright (C) 2008-2017 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. */
22 #define _GLTHREAD_COND_INLINE _GL_EXTERN_INLINE
23 #include "glthread/cond.h"
25 /* ========================================================================= */
29 /* -------------------------- gl_cond_t datatype -------------------------- */
32 glthread_cond_timedwait_multithreaded (gl_cond_t
*cond
,
34 struct timespec
*abstime
)
39 ev
= pth_event (PTH_EVENT_TIME
, pth_time (abstime
->tv_sec
, abstime
->tv_nsec
/ 1000));
40 ret
= pth_cond_await (cond
, lock
, ev
);
42 status
= pth_event_status (ev
);
43 pth_event_free (ev
, PTH_FREE_THIS
);
45 if (status
== PTH_STATUS_OCCURRED
)
53 /* ========================================================================= */
55 #if USE_SOLARIS_THREADS
57 /* -------------------------- gl_cond_t datatype -------------------------- */
60 glthread_cond_timedwait_multithreaded (gl_cond_t
*cond
,
62 struct timespec
*abstime
)
66 ret
= cond_timedwait (cond
, lock
, abstime
);
74 /* ========================================================================= */
76 #if USE_WINDOWS_THREADS
80 /* -------------------------- gl_cond_t datatype -------------------------- */
82 /* In this file, the waitqueues are implemented as linked lists. */
83 #define gl_waitqueue_t gl_linked_waitqueue_t
85 /* All links of a circular list, except the anchor, are of this type, carrying
87 struct gl_waitqueue_element
89 struct gl_waitqueue_link link
; /* must be the first field! */
90 HANDLE event
; /* Waiting thread, represented by an event.
91 This field is immutable once initialized. */
95 gl_waitqueue_init (gl_waitqueue_t
*wq
)
97 wq
->wq_list
.wql_next
= &wq
->wq_list
;
98 wq
->wq_list
.wql_prev
= &wq
->wq_list
;
101 /* Enqueues the current thread, represented by an event, in a wait queue.
102 Returns NULL if an allocation failure occurs. */
103 static struct gl_waitqueue_element
*
104 gl_waitqueue_add (gl_waitqueue_t
*wq
)
106 struct gl_waitqueue_element
*elt
;
109 /* Allocate the memory for the waitqueue element on the heap, not on the
110 thread's stack. If the thread exits unexpectedly, we prefer to leak
111 some memory rather than to access unavailable memory and crash. */
113 (struct gl_waitqueue_element
*)
114 malloc (sizeof (struct gl_waitqueue_element
));
116 /* No more memory. */
119 /* Whether the created event is a manual-reset one or an auto-reset one,
120 does not matter, since we will wait on it only once. */
121 event
= CreateEvent (NULL
, TRUE
, FALSE
, NULL
);
122 if (event
== INVALID_HANDLE_VALUE
)
124 /* No way to allocate an event. */
129 /* Insert elt at the end of the circular list. */
130 (elt
->link
.wql_prev
= wq
->wq_list
.wql_prev
)->wql_next
= &elt
->link
;
131 (elt
->link
.wql_next
= &wq
->wq_list
)->wql_prev
= &elt
->link
;
135 /* Removes the current thread, represented by a 'struct gl_waitqueue_element *',
137 Returns true if is was found and removed, false if it was not present. */
139 gl_waitqueue_remove (gl_waitqueue_t
*wq
, struct gl_waitqueue_element
*elt
)
141 if (elt
->link
.wql_next
!= NULL
&& elt
->link
.wql_prev
!= NULL
)
143 /* Remove elt from the circular list. */
144 struct gl_waitqueue_link
*prev
= elt
->link
.wql_prev
;
145 struct gl_waitqueue_link
*next
= elt
->link
.wql_next
;
146 prev
->wql_next
= next
;
147 next
->wql_prev
= prev
;
148 elt
->link
.wql_next
= NULL
;
149 elt
->link
.wql_prev
= NULL
;
156 /* Notifies the first thread from a wait queue and dequeues it. */
158 gl_waitqueue_notify_first (gl_waitqueue_t
*wq
)
160 if (wq
->wq_list
.wql_next
!= &wq
->wq_list
)
162 struct gl_waitqueue_element
*elt
=
163 (struct gl_waitqueue_element
*) wq
->wq_list
.wql_next
;
164 struct gl_waitqueue_link
*prev
;
165 struct gl_waitqueue_link
*next
;
167 /* Remove elt from the circular list. */
168 prev
= &wq
->wq_list
; /* = elt->link.wql_prev; */
169 next
= elt
->link
.wql_next
;
170 prev
->wql_next
= next
;
171 next
->wql_prev
= prev
;
172 elt
->link
.wql_next
= NULL
;
173 elt
->link
.wql_prev
= NULL
;
175 SetEvent (elt
->event
);
176 /* After the SetEvent, this thread cannot access *elt any more, because
177 the woken-up thread will quickly call free (elt). */
181 /* Notifies all threads from a wait queue and dequeues them all. */
183 gl_waitqueue_notify_all (gl_waitqueue_t
*wq
)
185 struct gl_waitqueue_link
*l
;
187 for (l
= wq
->wq_list
.wql_next
; l
!= &wq
->wq_list
; )
189 struct gl_waitqueue_element
*elt
= (struct gl_waitqueue_element
*) l
;
190 struct gl_waitqueue_link
*prev
;
191 struct gl_waitqueue_link
*next
;
193 /* Remove elt from the circular list. */
194 prev
= &wq
->wq_list
; /* = elt->link.wql_prev; */
195 next
= elt
->link
.wql_next
;
196 prev
->wql_next
= next
;
197 next
->wql_prev
= prev
;
198 elt
->link
.wql_next
= NULL
;
199 elt
->link
.wql_prev
= NULL
;
201 SetEvent (elt
->event
);
202 /* After the SetEvent, this thread cannot access *elt any more, because
203 the woken-up thread will quickly call free (elt). */
207 if (!(wq
->wq_list
.wql_next
== &wq
->wq_list
208 && wq
->wq_list
.wql_prev
== &wq
->wq_list
))
213 glthread_cond_init_func (gl_cond_t
*cond
)
215 InitializeCriticalSection (&cond
->lock
);
216 gl_waitqueue_init (&cond
->waiters
);
218 cond
->guard
.done
= 1;
223 glthread_cond_wait_func (gl_cond_t
*cond
, gl_lock_t
*lock
)
225 if (!cond
->guard
.done
)
227 if (InterlockedIncrement (&cond
->guard
.started
) == 0)
228 /* This thread is the first one to need this condition variable.
230 glthread_cond_init (cond
);
232 /* Yield the CPU while waiting for another thread to finish
233 initializing this condition variable. */
234 while (!cond
->guard
.done
)
238 EnterCriticalSection (&cond
->lock
);
240 struct gl_waitqueue_element
*elt
= gl_waitqueue_add (&cond
->waiters
);
241 LeaveCriticalSection (&cond
->lock
);
244 /* Allocation failure. Weird. */
249 HANDLE event
= elt
->event
;
253 /* Now release the lock and let any other thread take it. */
254 err
= glthread_lock_unlock (lock
);
257 EnterCriticalSection (&cond
->lock
);
258 gl_waitqueue_remove (&cond
->waiters
, elt
);
259 LeaveCriticalSection (&cond
->lock
);
265 "If another thread is able to acquire the mutex after the
266 about-to-block thread has released it, then a subsequent call to
267 pthread_cond_broadcast() or pthread_cond_signal() in that thread
268 shall behave as if it were issued after the about-to-block thread
270 This is fulfilled here, because the thread signalling is done
271 through SetEvent, not PulseEvent. */
272 /* Wait until another thread signals this event. */
273 result
= WaitForSingleObject (event
, INFINITE
);
274 if (result
== WAIT_FAILED
|| result
== WAIT_TIMEOUT
)
278 /* The thread which signalled the event already did the bookkeeping:
279 removed us from the waiters. */
280 return glthread_lock_lock (lock
);
286 glthread_cond_timedwait_func (gl_cond_t
*cond
, gl_lock_t
*lock
, struct timespec
*abstime
)
288 struct timeval currtime
;
290 gettimeofday (&currtime
, NULL
);
291 if (currtime
.tv_sec
> abstime
->tv_sec
292 || (currtime
.tv_sec
== abstime
->tv_sec
293 && currtime
.tv_usec
* 1000 >= abstime
->tv_nsec
))
296 if (!cond
->guard
.done
)
298 if (InterlockedIncrement (&cond
->guard
.started
) == 0)
299 /* This thread is the first one to need this condition variable.
301 glthread_cond_init (cond
);
303 /* Yield the CPU while waiting for another thread to finish
304 initializing this condition variable. */
305 while (!cond
->guard
.done
)
309 EnterCriticalSection (&cond
->lock
);
311 struct gl_waitqueue_element
*elt
= gl_waitqueue_add (&cond
->waiters
);
312 LeaveCriticalSection (&cond
->lock
);
315 /* Allocation failure. Weird. */
320 HANDLE event
= elt
->event
;
325 /* Now release the lock and let any other thread take it. */
326 err
= glthread_lock_unlock (lock
);
329 EnterCriticalSection (&cond
->lock
);
330 gl_waitqueue_remove (&cond
->waiters
, elt
);
331 LeaveCriticalSection (&cond
->lock
);
337 "If another thread is able to acquire the mutex after the
338 about-to-block thread has released it, then a subsequent call to
339 pthread_cond_broadcast() or pthread_cond_signal() in that thread
340 shall behave as if it were issued after the about-to-block thread
342 This is fulfilled here, because the thread signalling is done
343 through SetEvent, not PulseEvent. */
344 /* Wait until another thread signals this event or until the abstime
346 gettimeofday (&currtime
, NULL
);
347 if (currtime
.tv_sec
> abstime
->tv_sec
)
351 unsigned long seconds
= abstime
->tv_sec
- currtime
.tv_sec
;
352 timeout
= seconds
* 1000;
353 if (timeout
/ 1000 != seconds
) /* overflow? */
358 abstime
->tv_nsec
/ 1000000 - currtime
.tv_usec
/ 1000;
359 if (milliseconds
>= 0)
361 timeout
+= milliseconds
;
362 if (timeout
< milliseconds
) /* overflow? */
367 if (timeout
>= - milliseconds
)
368 timeout
-= (- milliseconds
);
374 result
= WaitForSingleObject (event
, timeout
);
375 if (result
== WAIT_FAILED
)
377 if (result
== WAIT_TIMEOUT
)
379 EnterCriticalSection (&cond
->lock
);
380 if (gl_waitqueue_remove (&cond
->waiters
, elt
))
382 /* The event was not signaled between the WaitForSingleObject
383 call and the EnterCriticalSection call. */
384 if (!(WaitForSingleObject (event
, 0) == WAIT_TIMEOUT
))
389 /* The event was signaled between the WaitForSingleObject
390 call and the EnterCriticalSection call. */
391 if (!(WaitForSingleObject (event
, 0) == WAIT_OBJECT_0
))
393 /* Produce the right return value. */
394 result
= WAIT_OBJECT_0
;
396 LeaveCriticalSection (&cond
->lock
);
400 /* The thread which signalled the event already did the
401 bookkeeping: removed us from the waiters. */
405 /* Take the lock again. It does not matter whether this is done
406 before or after the bookkeeping for WAIT_TIMEOUT. */
407 err
= glthread_lock_lock (lock
);
409 result
== WAIT_OBJECT_0
? 0 :
410 result
== WAIT_TIMEOUT
? ETIMEDOUT
:
411 /* WAIT_FAILED shouldn't happen */ EAGAIN
);
417 glthread_cond_signal_func (gl_cond_t
*cond
)
419 if (!cond
->guard
.done
)
422 EnterCriticalSection (&cond
->lock
);
424 "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
425 have no effect if there are no threads currently blocked on cond." */
426 if (cond
->waiters
.wq_list
.wql_next
!= &cond
->waiters
.wq_list
)
427 gl_waitqueue_notify_first (&cond
->waiters
);
428 LeaveCriticalSection (&cond
->lock
);
434 glthread_cond_broadcast_func (gl_cond_t
*cond
)
436 if (!cond
->guard
.done
)
439 EnterCriticalSection (&cond
->lock
);
441 "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
442 have no effect if there are no threads currently blocked on cond."
443 gl_waitqueue_notify_all is a nop in this case. */
444 gl_waitqueue_notify_all (&cond
->waiters
);
445 LeaveCriticalSection (&cond
->lock
);
451 glthread_cond_destroy_func (gl_cond_t
*cond
)
453 if (!cond
->guard
.done
)
455 if (cond
->waiters
.wq_list
.wql_next
!= &cond
->waiters
.wq_list
)
457 DeleteCriticalSection (&cond
->lock
);
458 cond
->guard
.done
= 0;
464 /* ========================================================================= */