5 * This translation unit implements condition variables and their primitives.
8 * --------------------------------------------------------------------------
10 * Pthreads-win32 - POSIX Threads Library for Win32
11 * Copyright(C) 1998 John E. Bossom
12 * Copyright(C) 1999,2005 Pthreads-win32 contributors
14 * Contact Email: rpj@callisto.canberra.edu.au
16 * The current list of contributors is contained
17 * in the file CONTRIBUTORS included with the source
18 * code distribution. The list can also be seen at the
19 * following World Wide Web location:
20 * http://sources.redhat.com/pthreads-win32/contributors.html
22 * This library is free software; you can redistribute it and/or
23 * modify it under the terms of the GNU Lesser General Public
24 * License as published by the Free Software Foundation; either
25 * version 2 of the License, or (at your option) any later version.
27 * This library is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30 * Lesser General Public License for more details.
32 * You should have received a copy of the GNU Lesser General Public
33 * License along with this library in the file COPYING.LIB;
34 * if not, write to the Free Software Foundation, Inc.,
35 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
37 * -------------------------------------------------------------
39 * The algorithm used in this implementation is that developed by
40 * Alexander Terekhov in colaboration with Louis Thomas. The bulk
41 * of the discussion is recorded in the file README.CV, which contains
42 * several generations of both colaborators original algorithms. The final
43 * algorithm used here is the one referred to as
45 * Algorithm 8a / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ALL
47 * presented below in pseudo-code as it appeared:
51 * semBlockLock - bin.semaphore
52 * semBlockQueue - semaphore
53 * mtxExternal - mutex or CS
54 * mtxUnblockLock - mutex or CS
56 * nWaitersBlocked - int
57 * nWaitersToUnblock - int
61 * [auto: register int result ] // error checking omitted
62 * [auto: register int nSignalsWasLeft ]
63 * [auto: register int nWaitersWasGone ]
65 * sem_wait( semBlockLock );
67 * sem_post( semBlockLock );
69 * unlock( mtxExternal );
70 * bTimedOut = sem_wait( semBlockQueue,timeout );
72 * lock( mtxUnblockLock );
73 * if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) {
74 * if ( bTimeout ) { // timeout (or canceled)
75 * if ( 0 != nWaitersBlocked ) {
79 * nWaitersGone++; // count spurious wakeups.
82 * if ( 0 == --nWaitersToUnblock ) {
83 * if ( 0 != nWaitersBlocked ) {
84 * sem_post( semBlockLock ); // open the gate.
85 * nSignalsWasLeft = 0; // do not open the gate
88 * else if ( 0 != (nWaitersWasGone = nWaitersGone) ) {
93 * else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or
94 * // spurious semaphore :-)
95 * sem_wait( semBlockLock );
96 * nWaitersBlocked -= nWaitersGone; // something is going on here
97 * // - test of timeouts? :-)
98 * sem_post( semBlockLock );
101 * unlock( mtxUnblockLock );
103 * if ( 1 == nSignalsWasLeft ) {
104 * if ( 0 != nWaitersWasGone ) {
105 * // sem_adjust( semBlockQueue,-nWaitersWasGone );
106 * while ( nWaitersWasGone-- ) {
107 * sem_wait( semBlockQueue ); // better now than spurious later
109 * } sem_post( semBlockLock ); // open the gate
112 * lock( mtxExternal );
114 * return ( bTimedOut ) ? ETIMEOUT : 0;
119 * [auto: register int result ]
120 * [auto: register int nSignalsToIssue]
122 * lock( mtxUnblockLock );
124 * if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!
125 * if ( 0 == nWaitersBlocked ) { // NO-OP
126 * return unlock( mtxUnblockLock );
129 * nWaitersToUnblock += nSignalsToIssue=nWaitersBlocked;
130 * nWaitersBlocked = 0;
133 * nSignalsToIssue = 1;
134 * nWaitersToUnblock++;
138 * else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!
139 * sem_wait( semBlockLock ); // close the gate
140 * if ( 0 != nWaitersGone ) {
141 * nWaitersBlocked -= nWaitersGone;
145 * nSignalsToIssue = nWaitersToUnblock = nWaitersBlocked;
146 * nWaitersBlocked = 0;
149 * nSignalsToIssue = nWaitersToUnblock = 1;
154 * return unlock( mtxUnblockLock );
157 * unlock( mtxUnblockLock );
158 * sem_post( semBlockQueue,nSignalsToIssue );
161 * -------------------------------------------------------------
163 * Algorithm 9 / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ALL
165 * presented below in pseudo-code; basically 8a...
166 * ...BUT W/O "spurious wakes" prevention:
170 * semBlockLock - bin.semaphore
171 * semBlockQueue - semaphore
172 * mtxExternal - mutex or CS
173 * mtxUnblockLock - mutex or CS
175 * nWaitersBlocked - int
176 * nWaitersToUnblock - int
180 * [auto: register int result ] // error checking omitted
181 * [auto: register int nSignalsWasLeft ]
183 * sem_wait( semBlockLock );
185 * sem_post( semBlockLock );
187 * unlock( mtxExternal );
188 * bTimedOut = sem_wait( semBlockQueue,timeout );
190 * lock( mtxUnblockLock );
191 * if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) {
192 * --nWaitersToUnblock;
194 * else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or
195 * // spurious semaphore :-)
196 * sem_wait( semBlockLock );
197 * nWaitersBlocked -= nWaitersGone; // something is going on here
198 * // - test of timeouts? :-)
199 * sem_post( semBlockLock );
202 * unlock( mtxUnblockLock );
204 * if ( 1 == nSignalsWasLeft ) {
205 * sem_post( semBlockLock ); // open the gate
208 * lock( mtxExternal );
210 * return ( bTimedOut ) ? ETIMEOUT : 0;
215 * [auto: register int result ]
216 * [auto: register int nSignalsToIssue]
218 * lock( mtxUnblockLock );
220 * if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!
221 * if ( 0 == nWaitersBlocked ) { // NO-OP
222 * return unlock( mtxUnblockLock );
225 * nWaitersToUnblock += nSignalsToIssue=nWaitersBlocked;
226 * nWaitersBlocked = 0;
229 * nSignalsToIssue = 1;
230 * ++nWaitersToUnblock;
234 * else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!
235 * sem_wait( semBlockLock ); // close the gate
236 * if ( 0 != nWaitersGone ) {
237 * nWaitersBlocked -= nWaitersGone;
241 * nSignalsToIssue = nWaitersToUnblock = nWaitersBlocked;
242 * nWaitersBlocked = 0;
245 * nSignalsToIssue = nWaitersToUnblock = 1;
250 * return unlock( mtxUnblockLock );
253 * unlock( mtxUnblockLock );
254 * sem_post( semBlockQueue,nSignalsToIssue );
257 * -------------------------------------------------------------
262 #include "implement.h"
265 * Arguments for cond_wait_cleanup, since we can only pass a
266 * single void * to it.
270 pthread_mutex_t
*mutexPtr
;
273 } ptw32_cond_wait_cleanup_args_t
;
275 static void PTW32_CDECL
276 ptw32_cond_wait_cleanup (void *args
)
278 ptw32_cond_wait_cleanup_args_t
*cleanup_args
=
279 (ptw32_cond_wait_cleanup_args_t
*) args
;
280 pthread_cond_t cv
= cleanup_args
->cv
;
281 int *resultPtr
= cleanup_args
->resultPtr
;
286 * Whether we got here as a result of signal/broadcast or because of
287 * timeout on wait or thread cancellation we indicate that we are no
288 * longer waiting. The waiter is responsible for adjusting waiters
289 * (to)unblock(ed) counts (protected by unblock lock).
291 if ((result
= pthread_mutex_lock (&(cv
->mtxUnblockLock
))) != 0)
297 if (0 != (nSignalsWasLeft
= cv
->nWaitersToUnblock
))
299 --(cv
->nWaitersToUnblock
);
301 else if (INT_MAX
/ 2 == ++(cv
->nWaitersGone
))
303 /* Use the non-cancellable version of sem_wait() */
304 if (ptw32_semwait (&(cv
->semBlockLock
)) != 0)
308 * This is a fatal error for this CV,
309 * so we deliberately don't unlock
310 * cv->mtxUnblockLock before returning.
314 cv
->nWaitersBlocked
-= cv
->nWaitersGone
;
315 if (sem_post (&(cv
->semBlockLock
)) != 0)
319 * This is a fatal error for this CV,
320 * so we deliberately don't unlock
321 * cv->mtxUnblockLock before returning.
325 cv
->nWaitersGone
= 0;
328 if ((result
= pthread_mutex_unlock (&(cv
->mtxUnblockLock
))) != 0)
334 if (1 == nSignalsWasLeft
)
336 if (sem_post (&(cv
->semBlockLock
)) != 0)
344 * XSH: Upon successful return, the mutex has been locked and is owned
345 * by the calling thread.
347 if ((result
= pthread_mutex_lock (cleanup_args
->mutexPtr
)) != 0)
351 } /* ptw32_cond_wait_cleanup */
354 ptw32_cond_timedwait (pthread_cond_t
* cond
,
355 pthread_mutex_t
* mutex
, const struct timespec
*abstime
)
359 ptw32_cond_wait_cleanup_args_t cleanup_args
;
361 if (cond
== NULL
|| *cond
== NULL
)
367 * We do a quick check to see if we need to do more work
368 * to initialise a static condition variable. We check
369 * again inside the guarded section of ptw32_cond_check_need_init()
370 * to avoid race conditions.
372 if (*cond
== PTHREAD_COND_INITIALIZER
)
374 result
= ptw32_cond_check_need_init (cond
);
377 if (result
!= 0 && result
!= EBUSY
)
384 /* Thread can be cancelled in sem_wait() but this is OK */
385 if (sem_wait (&(cv
->semBlockLock
)) != 0)
390 ++(cv
->nWaitersBlocked
);
392 if (sem_post (&(cv
->semBlockLock
)) != 0)
398 * Setup this waiter cleanup handler
400 cleanup_args
.mutexPtr
= mutex
;
401 cleanup_args
.cv
= cv
;
402 cleanup_args
.resultPtr
= &result
;
404 #if defined(_MSC_VER) && _MSC_VER < 1400
405 #pragma inline_depth(0)
407 pthread_cleanup_push (ptw32_cond_wait_cleanup
, (void *) &cleanup_args
);
410 * Now we can release 'mutex' and...
412 if ((result
= pthread_mutex_unlock (mutex
)) == 0)
416 * ...wait to be awakened by
417 * pthread_cond_signal, or
418 * pthread_cond_broadcast, or
420 * thread cancellation
424 * sem_timedwait is a cancellation point,
425 * hence providing the mechanism for making
426 * pthread_cond_wait a cancellation point.
427 * We use the cleanup mechanism to ensure we
428 * re-lock the mutex and adjust (to)unblock(ed) waiters
429 * counts if we are cancelled, timed out or signalled.
431 if (sem_timedwait (&(cv
->semBlockQueue
), abstime
) != 0)
440 pthread_cleanup_pop (1);
441 #if defined(_MSC_VER) && _MSC_VER < 1400
442 #pragma inline_depth()
446 * "result" can be modified by the cleanup handler.
450 } /* ptw32_cond_timedwait */
454 pthread_cond_wait (pthread_cond_t
* cond
, pthread_mutex_t
* mutex
)
456 * ------------------------------------------------------
458 * This function waits on a condition variable until
459 * awakened by a signal or broadcast.
461 * Caller MUST be holding the mutex lock; the
462 * lock is released and the caller is blocked waiting
463 * on 'cond'. When 'cond' is signaled, the mutex
464 * is re-acquired before returning to the caller.
468 * pointer to an instance of pthread_cond_t
471 * pointer to an instance of pthread_mutex_t
475 * This function waits on a condition variable until
476 * awakened by a signal or broadcast.
480 * 1) The function must be called with 'mutex' LOCKED
481 * by the calling thread, or undefined behaviour
484 * 2) This routine atomically releases 'mutex' and causes
485 * the calling thread to block on the condition variable.
486 * The blocked thread may be awakened by
487 * pthread_cond_signal or
488 * pthread_cond_broadcast.
490 * Upon successful completion, the 'mutex' has been locked and
491 * is owned by the calling thread.
495 * 0 caught condition; mutex released,
496 * EINVAL 'cond' or 'mutex' is invalid,
497 * EINVAL different mutexes for concurrent waits,
498 * EINVAL mutex is not held by the calling thread,
500 * ------------------------------------------------------
504 * The NULL abstime arg means INFINITE waiting.
506 return (ptw32_cond_timedwait (cond
, mutex
, NULL
));
508 } /* pthread_cond_wait */
512 pthread_cond_timedwait (pthread_cond_t
* cond
,
513 pthread_mutex_t
* mutex
,
514 const struct timespec
*abstime
)
516 * ------------------------------------------------------
518 * This function waits on a condition variable either until
519 * awakened by a signal or broadcast; or until the time
520 * specified by abstime passes.
524 * pointer to an instance of pthread_cond_t
527 * pointer to an instance of pthread_mutex_t
530 * pointer to an instance of (const struct timespec)
534 * This function waits on a condition variable either until
535 * awakened by a signal or broadcast; or until the time
536 * specified by abstime passes.
539 * 1) The function must be called with 'mutex' LOCKED
540 * by the calling thread, or undefined behaviour
543 * 2) This routine atomically releases 'mutex' and causes
544 * the calling thread to block on the condition variable.
545 * The blocked thread may be awakened by
546 * pthread_cond_signal or
547 * pthread_cond_broadcast.
551 * 0 caught condition; mutex released,
552 * EINVAL 'cond', 'mutex', or abstime is invalid,
553 * EINVAL different mutexes for concurrent waits,
554 * EINVAL mutex is not held by the calling thread,
555 * ETIMEDOUT abstime ellapsed before cond was signaled.
557 * ------------------------------------------------------
565 return (ptw32_cond_timedwait (cond
, mutex
, abstime
));
567 } /* pthread_cond_timedwait */