1 /* Condition variables for multithreading.
2 Copyright (C) 2008 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, write to the Free Software Foundation,
16 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
18 /* Written by Yoann Vandoorselaere <yoann@prelude-ids.org>, 2008,
19 and Bruno Haible <bruno@clisp.org>, 2008. */
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 /* ========================================================================= */
80 /* -------------------------- gl_cond_t datatype -------------------------- */
82 /* This implementation is based on the article
83 Douglas C. Schmidt, Irfan Pyarali
84 "Strategies for Implementing POSIX Condition Variables on Win32"
85 <http://www.cs.wustl.edu/~schmidt/win32-cv-1.html> */
88 glthread_cond_init_func (gl_cond_t *cond)
90 InitializeCriticalSection (&cond->lock);
91 /* Create a manual-reset event. */
92 cond->event = CreateEvent (NULL, TRUE, FALSE, NULL);
93 cond->waiters_count = 0;
94 cond->release_count = 0;
95 cond->wait_generation_count = 0;
103 glthread_cond_wait_func (gl_cond_t *cond, gl_lock_t *lock)
105 if (!cond->guard.done)
107 if (InterlockedIncrement (&cond->guard.started) == 0)
108 /* This thread is the first one to need this condition variable.
110 glthread_cond_init (cond);
112 /* Yield the CPU while waiting for another thread to finish
113 initializing this condition variable. */
114 while (!cond->guard.done)
119 unsigned old_generation_count;
122 EnterCriticalSection (&cond->lock);
123 /* Increment waiters_count,
124 and get a copy of the current wait_generation_count. */
125 cond->waiters_count++;
126 old_generation_count = cond->wait_generation_count;
128 LeaveCriticalSection (&cond->lock);
131 /* Now release the lock and let any other thread take it. */
132 int err = glthread_lock_unlock (lock);
135 EnterCriticalSection (&cond->lock);
136 cond->waiters_count--;
137 LeaveCriticalSection (&cond->lock);
142 /* Wait until another thread signals this event. */
149 result = WaitForSingleObject (event, INFINITE);
150 if (result != WAIT_OBJECT_0)
152 /* Distingish intended from spurious wakeups. */
153 EnterCriticalSection (&cond->lock);
155 (cond->release_count > 0
156 && cond->wait_generation_count != old_generation_count);
157 LeaveCriticalSection (&cond->lock);
162 /* Take the lock again. */
163 err = glthread_lock_lock (lock);
165 /* Do the bookkeeping. */
166 EnterCriticalSection (&cond->lock);
167 cond->waiters_count--;
168 if (result == WAIT_OBJECT_0)
170 /* The wait terminated because the event was signaled.
171 Acknowledge the receipt. */
172 if (--cond->release_count == 0)
174 /* The last waiting thread to be notified has to reset
176 ResetEvent (cond->event);
179 LeaveCriticalSection (&cond->lock);
182 result == WAIT_OBJECT_0 ? 0 :
183 /* WAIT_FAILED shouldn't happen */ EAGAIN);
190 glthread_cond_timedwait_func (gl_cond_t *cond, gl_lock_t *lock, struct timespec *abstime)
192 struct timeval currtime;
194 gettimeofday (&currtime, NULL);
195 if (currtime.tv_sec > abstime->tv_sec
196 || (currtime.tv_sec == abstime->tv_sec
197 && currtime.tv_usec * 1000 >= abstime->tv_nsec))
200 if (!cond->guard.done)
202 if (InterlockedIncrement (&cond->guard.started) == 0)
203 /* This thread is the first one to need this condition variable.
205 glthread_cond_init (cond);
207 /* Yield the CPU while waiting for another thread to finish
208 initializing this condition variable. */
209 while (!cond->guard.done)
214 unsigned old_generation_count;
217 EnterCriticalSection (&cond->lock);
218 /* Increment waiters_count,
219 and get a copy of the current wait_generation_count. */
220 cond->waiters_count++;
221 old_generation_count = cond->wait_generation_count;
223 LeaveCriticalSection (&cond->lock);
226 /* Now release the lock and let any other thread take it. */
227 int err = glthread_lock_unlock (lock);
230 EnterCriticalSection (&cond->lock);
231 cond->waiters_count--;
232 LeaveCriticalSection (&cond->lock);
237 /* Wait until another thread signals this event or until the abstime
246 gettimeofday (&currtime, NULL);
247 if (currtime.tv_sec > abstime->tv_sec)
251 unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
252 timeout = seconds * 1000;
253 if (timeout / 1000 != seconds) /* overflow? */
258 abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
259 if (milliseconds >= 0)
261 timeout += milliseconds;
262 if (timeout < milliseconds) /* overflow? */
267 if (timeout >= - milliseconds)
268 timeout -= (- milliseconds);
275 result = WaitForSingleObject (event, timeout);
276 if (result != WAIT_OBJECT_0)
278 /* Distingish intended from spurious wakeups. */
279 EnterCriticalSection (&cond->lock);
281 (cond->release_count > 0
282 && cond->wait_generation_count != old_generation_count);
283 LeaveCriticalSection (&cond->lock);
288 /* Take the lock again. */
289 err = glthread_lock_lock (lock);
291 /* Do the bookkeeping. */
292 EnterCriticalSection (&cond->lock);
293 cond->waiters_count--;
294 if (result == WAIT_OBJECT_0)
296 /* The wait terminated because the event was signaled.
297 Acknowledge the receipt. */
298 if (--cond->release_count == 0)
300 /* The last waiting thread to be notified has to reset
302 ResetEvent (cond->event);
305 LeaveCriticalSection (&cond->lock);
308 result == WAIT_OBJECT_0 ? 0 :
309 result == WAIT_TIMEOUT ? ETIMEDOUT :
310 /* WAIT_FAILED shouldn't happen */ EAGAIN);
317 glthread_cond_signal_func (gl_cond_t *cond)
319 if (!cond->guard.done)
322 EnterCriticalSection (&cond->lock);
324 "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
325 have no effect if there are no threads currently blocked on cond."
326 Also, if waiters_count == release_count > 0, all blocked threads will
327 be unblocked soon anyway; do nothing in this case as well. */
328 if (cond->waiters_count > cond->release_count)
330 /* Signal the manual-reset event. */
331 SetEvent (cond->event);
332 /* ... and reset it is soon as one of the currently waiting threads has
333 acknowledged the receipt of the signal. */
334 cond->release_count++;
335 cond->wait_generation_count++;
337 LeaveCriticalSection (&cond->lock);
343 glthread_cond_broadcast_func (gl_cond_t *cond)
345 if (!cond->guard.done)
348 EnterCriticalSection (&cond->lock);
350 "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
351 have no effect if there are no threads currently blocked on cond." */
352 if (cond->waiters_count > 0)
354 /* Signal the manual-reset event. */
355 SetEvent (cond->event);
356 /* ... and reset it only after all currently waiting threads have
357 acknowledged the receipt of the signal. */
358 cond->release_count = cond->waiters_count;
359 cond->wait_generation_count++;
361 LeaveCriticalSection (&cond->lock);
367 glthread_cond_destroy_func (gl_cond_t *cond)
369 if (!cond->guard.done)
371 if (cond->waiters_count > 0)
373 CloseHandle (cond->event);
374 DeleteCriticalSection (&cond->lock);
375 cond->guard.done = 0;
381 /* ========================================================================= */