1 /*
2 * pthread_cond_wait.c
3 *
4 * Description:
5 * This translation unit implements condition variables and their primitives.
6 *
7 *
8 * --------------------------------------------------------------------------
9 *
10 * Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
11 * Copyright(C) 2008 Jason Schmidlapp
12 *
13 * Contact Email: jschmidlapp@users.sourceforge.net
14 *
15 *
16 * Based upon Pthreads-win32 - POSIX Threads Library for Win32
17 * Copyright(C) 1998 John E. Bossom
18 * Copyright(C) 1999,2005 Pthreads-win32 contributors
19 *
20 * Contact Email: rpj@callisto.canberra.edu.au
21 *
22 * The original list of contributors to the Pthreads-win32 project
23 * is contained in the file CONTRIBUTORS.ptw32 included with the
24 * source code distribution. The list can also be seen at the
25 * following World Wide Web location:
26 * http://sources.redhat.com/pthreads-win32/contributors.html
27 *
28 * This library is free software; you can redistribute it and/or
29 * modify it under the terms of the GNU Lesser General Public
30 * License as published by the Free Software Foundation; either
31 * version 2 of the License, or (at your option) any later version.
32 *
33 * This library is distributed in the hope that it will be useful,
34 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
36 * Lesser General Public License for more details.
37 *
38 * You should have received a copy of the GNU Lesser General Public
39 * License along with this library in the file COPYING.LIB;
40 * if not, write to the Free Software Foundation, Inc.,
41 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
42 *
43 * -------------------------------------------------------------
44 * Algorithm:
45 * The algorithm used in this implementation is that developed by
46 * Alexander Terekhov in colaboration with Louis Thomas. The bulk
47 * of the discussion is recorded in the file README.CV, which contains
48 * several generations of both colaborators original algorithms. The final
49 * algorithm used here is the one referred to as
50 *
51 * Algorithm 8a / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ALL
52 *
53 * presented below in pseudo-code as it appeared:
54 *
55 *
56 * given:
57 * semBlockLock - bin.semaphore
58 * semBlockQueue - semaphore
59 * mtxExternal - mutex or CS
60 * mtxUnblockLock - mutex or CS
61 * nWaitersGone - int
62 * nWaitersBlocked - int
63 * nWaitersToUnblock - int
64 *
65 * wait( timeout ) {
66 *
67 * [auto: register int result ] // error checking omitted
68 * [auto: register int nSignalsWasLeft ]
69 * [auto: register int nWaitersWasGone ]
70 *
71 * sem_wait( semBlockLock );
72 * nWaitersBlocked++;
73 * sem_post( semBlockLock );
74 *
75 * unlock( mtxExternal );
76 * bTimedOut = sem_wait( semBlockQueue,timeout );
77 *
78 * lock( mtxUnblockLock );
79 * if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) {
80 * if ( bTimeout ) { // timeout (or canceled)
81 * if ( 0 != nWaitersBlocked ) {
82 * nWaitersBlocked--;
83 * }
84 * else {
85 * nWaitersGone++; // count spurious wakeups.
86 * }
87 * }
88 * if ( 0 == --nWaitersToUnblock ) {
89 * if ( 0 != nWaitersBlocked ) {
90 * sem_post( semBlockLock ); // open the gate.
91 * nSignalsWasLeft = 0; // do not open the gate
92 * // below again.
93 * }
94 * else if ( 0 != (nWaitersWasGone = nWaitersGone) ) {
95 * nWaitersGone = 0;
96 * }
97 * }
98 * }
99 * else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or
100 * // spurious semaphore :-)
101 * sem_wait( semBlockLock );
102 * nWaitersBlocked -= nWaitersGone; // something is going on here
103 * // - test of timeouts? :-)
104 * sem_post( semBlockLock );
105 * nWaitersGone = 0;
106 * }
107 * unlock( mtxUnblockLock );
108 *
109 * if ( 1 == nSignalsWasLeft ) {
110 * if ( 0 != nWaitersWasGone ) {
111 * // sem_adjust( semBlockQueue,-nWaitersWasGone );
112 * while ( nWaitersWasGone-- ) {
113 * sem_wait( semBlockQueue ); // better now than spurious later
114 * }
115 * } sem_post( semBlockLock ); // open the gate
116 * }
117 *
118 * lock( mtxExternal );
119 *
120 * return ( bTimedOut ) ? ETIMEOUT : 0;
121 * }
122 *
123 * signal(bAll) {
124 *
125 * [auto: register int result ]
126 * [auto: register int nSignalsToIssue]
127 *
128 * lock( mtxUnblockLock );
129 *
130 * if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!
131 * if ( 0 == nWaitersBlocked ) { // NO-OP
132 * return unlock( mtxUnblockLock );
133 * }
134 * if (bAll) {
135 * nWaitersToUnblock += nSignalsToIssue=nWaitersBlocked;
136 * nWaitersBlocked = 0;
137 * }
138 * else {
139 * nSignalsToIssue = 1;
140 * nWaitersToUnblock++;
141 * nWaitersBlocked--;
142 * }
143 * }
144 * else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!
145 * sem_wait( semBlockLock ); // close the gate
146 * if ( 0 != nWaitersGone ) {
147 * nWaitersBlocked -= nWaitersGone;
148 * nWaitersGone = 0;
149 * }
150 * if (bAll) {
151 * nSignalsToIssue = nWaitersToUnblock = nWaitersBlocked;
152 * nWaitersBlocked = 0;
153 * }
154 * else {
155 * nSignalsToIssue = nWaitersToUnblock = 1;
156 * nWaitersBlocked--;
157 * }
158 * }
159 * else { // NO-OP
160 * return unlock( mtxUnblockLock );
161 * }
162 *
163 * unlock( mtxUnblockLock );
164 * sem_post( semBlockQueue,nSignalsToIssue );
165 * return result;
166 * }
167 * -------------------------------------------------------------
168 *
169 * Algorithm 9 / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ALL
170 *
171 * presented below in pseudo-code; basically 8a...
172 * ...BUT W/O "spurious wakes" prevention:
173 *
174 *
175 * given:
176 * semBlockLock - bin.semaphore
177 * semBlockQueue - semaphore
178 * mtxExternal - mutex or CS
179 * mtxUnblockLock - mutex or CS
180 * nWaitersGone - int
181 * nWaitersBlocked - int
182 * nWaitersToUnblock - int
183 *
184 * wait( timeout ) {
185 *
186 * [auto: register int result ] // error checking omitted
187 * [auto: register int nSignalsWasLeft ]
188 *
189 * sem_wait( semBlockLock );
190 * ++nWaitersBlocked;
191 * sem_post( semBlockLock );
192 *
193 * unlock( mtxExternal );
194 * bTimedOut = sem_wait( semBlockQueue,timeout );
195 *
196 * lock( mtxUnblockLock );
197 * if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) {
198 * --nWaitersToUnblock;
199 * }
200 * else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or
201 * // spurious semaphore :-)
202 * sem_wait( semBlockLock );
203 * nWaitersBlocked -= nWaitersGone; // something is going on here
204 * // - test of timeouts? :-)
205 * sem_post( semBlockLock );
206 * nWaitersGone = 0;
207 * }
208 * unlock( mtxUnblockLock );
209 *
210 * if ( 1 == nSignalsWasLeft ) {
211 * sem_post( semBlockLock ); // open the gate
212 * }
213 *
214 * lock( mtxExternal );
215 *
216 * return ( bTimedOut ) ? ETIMEOUT : 0;
217 * }
218 *
219 * signal(bAll) {
220 *
221 * [auto: register int result ]
222 * [auto: register int nSignalsToIssue]
223 *
224 * lock( mtxUnblockLock );
225 *
226 * if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!
227 * if ( 0 == nWaitersBlocked ) { // NO-OP
228 * return unlock( mtxUnblockLock );
229 * }
230 * if (bAll) {
231 * nWaitersToUnblock += nSignalsToIssue=nWaitersBlocked;
232 * nWaitersBlocked = 0;
233 * }
234 * else {
235 * nSignalsToIssue = 1;
236 * ++nWaitersToUnblock;
237 * --nWaitersBlocked;
238 * }
239 * }
240 * else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!
241 * sem_wait( semBlockLock ); // close the gate
242 * if ( 0 != nWaitersGone ) {
243 * nWaitersBlocked -= nWaitersGone;
244 * nWaitersGone = 0;
245 * }
246 * if (bAll) {
247 * nSignalsToIssue = nWaitersToUnblock = nWaitersBlocked;
248 * nWaitersBlocked = 0;
249 * }
250 * else {
251 * nSignalsToIssue = nWaitersToUnblock = 1;
252 * --nWaitersBlocked;
253 * }
254 * }
255 * else { // NO-OP
256 * return unlock( mtxUnblockLock );
257 * }
258 *
259 * unlock( mtxUnblockLock );
260 * sem_post( semBlockQueue,nSignalsToIssue );
261 * return result;
262 * }
263 * -------------------------------------------------------------
264 *
265 */
266
267 #include "pthread.h"
268 #include "implement.h"
269
270 /*
271 * Arguments for cond_wait_cleanup, since we can only pass a
272 * single void * to it.
273 */
274 typedef struct
275 {
276 pthread_mutex_t *mutexPtr;
277 pthread_cond_t cv;
278 int *resultPtr;
279 } pte_cond_wait_cleanup_args_t;
280
281 static void
pte_cond_wait_cleanup(void * args)282 pte_cond_wait_cleanup (void *args)
283 {
284 pte_cond_wait_cleanup_args_t *cleanup_args =
285 (pte_cond_wait_cleanup_args_t *) args;
286 pthread_cond_t cv = cleanup_args->cv;
287 int *resultPtr = cleanup_args->resultPtr;
288 int nSignalsWasLeft;
289 int result;
290
291 /*
292 * Whether we got here as a result of signal/broadcast or because of
293 * timeout on wait or thread cancellation we indicate that we are no
294 * longer waiting. The waiter is responsible for adjusting waiters
295 * (to)unblock(ed) counts (protected by unblock lock).
296 */
297 if ((result = pthread_mutex_lock (&(cv->mtxUnblockLock))) != 0)
298 {
299 *resultPtr = result;
300 return;
301 }
302
303 if (0 != (nSignalsWasLeft = cv->nWaitersToUnblock))
304 {
305 --(cv->nWaitersToUnblock);
306 }
307 else if (INT_MAX / 2 == ++(cv->nWaitersGone))
308 {
309 /* Use the non-cancellable version of sem_wait() */
310 // if (sem_wait_nocancel (&(cv->semBlockLock)) != 0)
311 if (sem_wait (&(cv->semBlockLock)) != 0)
312 {
313 *resultPtr = errno;
314 /*
315 * This is a fatal error for this CV,
316 * so we deliberately don't unlock
317 * cv->mtxUnblockLock before returning.
318 */
319 return;
320 }
321 cv->nWaitersBlocked -= cv->nWaitersGone;
322 if (sem_post (&(cv->semBlockLock)) != 0)
323 {
324 *resultPtr = errno;
325 /*
326 * This is a fatal error for this CV,
327 * so we deliberately don't unlock
328 * cv->mtxUnblockLock before returning.
329 */
330 return;
331 }
332 cv->nWaitersGone = 0;
333 }
334
335 if ((result = pthread_mutex_unlock (&(cv->mtxUnblockLock))) != 0)
336 {
337 *resultPtr = result;
338 return;
339 }
340
341 if (1 == nSignalsWasLeft)
342 {
343 if (sem_post (&(cv->semBlockLock)) != 0)
344 {
345 *resultPtr = errno;
346 return;
347 }
348 }
349
350 /*
351 * XSH: Upon successful return, the mutex has been locked and is owned
352 * by the calling thread.
353 */
354 if ((result = pthread_mutex_lock (cleanup_args->mutexPtr)) != 0)
355 {
356 *resultPtr = result;
357 }
358 } /* pte_cond_wait_cleanup */
359
360 static int
pte_cond_timedwait(pthread_cond_t * cond,pthread_mutex_t * mutex,const struct timespec * abstime)361 pte_cond_timedwait (pthread_cond_t * cond,
362 pthread_mutex_t * mutex, const struct timespec *abstime)
363 {
364 int result = 0;
365 pthread_cond_t cv;
366 pte_cond_wait_cleanup_args_t cleanup_args;
367
368 if (cond == NULL || *cond == NULL)
369 {
370 return EINVAL;
371 }
372
373 /*
374 * We do a quick check to see if we need to do more work
375 * to initialise a static condition variable. We check
376 * again inside the guarded section of pte_cond_check_need_init()
377 * to avoid race conditions.
378 */
379 if (*cond == PTHREAD_COND_INITIALIZER)
380 {
381 result = pte_cond_check_need_init (cond);
382 }
383
384 if (result != 0 && result != EBUSY)
385 {
386 return result;
387 }
388
389 cv = *cond;
390
391 /* Thread can be cancelled in sem_wait() but this is OK */
392 if (sem_wait (&(cv->semBlockLock)) != 0)
393 {
394 return errno;
395 }
396
397 ++(cv->nWaitersBlocked);
398
399 if (sem_post (&(cv->semBlockLock)) != 0)
400 {
401 return errno;
402 }
403
404 /*
405 * Setup this waiter cleanup handler
406 */
407 cleanup_args.mutexPtr = mutex;
408 cleanup_args.cv = cv;
409 cleanup_args.resultPtr = &result;
410
411 pthread_cleanup_push (pte_cond_wait_cleanup, (void *) &cleanup_args);
412
413 /*
414 * Now we can release 'mutex' and...
415 */
416 if ((result = pthread_mutex_unlock (mutex)) == 0)
417 {
418 /*
419 * ...wait to be awakened by
420 * pthread_cond_signal, or
421 * pthread_cond_broadcast, or
422 * timeout, or
423 * thread cancellation
424 *
425 * Note:
426 *
427 * sem_timedwait is a cancellation point,
428 * hence providing the mechanism for making
429 * pthread_cond_wait a cancellation point.
430 * We use the cleanup mechanism to ensure we
431 * re-lock the mutex and adjust (to)unblock(ed) waiters
432 * counts if we are cancelled, timed out or signalled.
433 */
434 if (sem_timedwait (&(cv->semBlockQueue), abstime) != 0)
435 {
436 result = errno;
437 }
438 }
439
440
441 /*
442 * Always cleanup
443 */
444 pthread_cleanup_pop (1);
445
446 /*
447 * "result" can be modified by the cleanup handler.
448 */
449 return result;
450
451 } /* pte_cond_timedwait */
452
453
454 int
pthread_cond_wait(pthread_cond_t * cond,pthread_mutex_t * mutex)455 pthread_cond_wait (pthread_cond_t * cond, pthread_mutex_t * mutex)
456 /*
457 * ------------------------------------------------------
458 * DOCPUBLIC
459 * This function waits on a condition variable until
460 * awakened by a signal or broadcast.
461 *
462 * Caller MUST be holding the mutex lock; the
463 * lock is released and the caller is blocked waiting
464 * on 'cond'. When 'cond' is signaled, the mutex
465 * is re-acquired before returning to the caller.
466 *
467 * PARAMETERS
468 * cond
469 * pointer to an instance of pthread_cond_t
470 *
471 * mutex
472 * pointer to an instance of pthread_mutex_t
473 *
474 *
475 * DESCRIPTION
476 * This function waits on a condition variable until
477 * awakened by a signal or broadcast.
478 *
479 * NOTES:
480 *
481 * 1) The function must be called with 'mutex' LOCKED
482 * by the calling thread, or undefined behaviour
483 * will result.
484 *
485 * 2) This routine atomically releases 'mutex' and causes
486 * the calling thread to block on the condition variable.
487 * The blocked thread may be awakened by
488 * pthread_cond_signal or
489 * pthread_cond_broadcast.
490 *
491 * Upon successful completion, the 'mutex' has been locked and
492 * is owned by the calling thread.
493 *
494 *
495 * RESULTS
496 * 0 caught condition; mutex released,
497 * EINVAL 'cond' or 'mutex' is invalid,
498 * EINVAL different mutexes for concurrent waits,
499 * EINVAL mutex is not held by the calling thread,
500 *
501 * ------------------------------------------------------
502 */
503 {
504 /*
505 * The NULL abstime arg means INFINITE waiting.
506 */
507 return (pte_cond_timedwait (cond, mutex, NULL));
508
509 } /* pthread_cond_wait */
510
511
512 int
pthread_cond_timedwait(pthread_cond_t * cond,pthread_mutex_t * mutex,const struct timespec * abstime)513 pthread_cond_timedwait (pthread_cond_t * cond,
514 pthread_mutex_t * mutex,
515 const struct timespec *abstime)
516 /*
517 * ------------------------------------------------------
518 * DOCPUBLIC
519 * This function waits on a condition variable either until
520 * awakened by a signal or broadcast; or until the time
521 * specified by abstime passes.
522 *
523 * PARAMETERS
524 * cond
525 * pointer to an instance of pthread_cond_t
526 *
527 * mutex
528 * pointer to an instance of pthread_mutex_t
529 *
530 * abstime
531 * pointer to an instance of (const struct timespec)
532 *
533 *
534 * DESCRIPTION
535 * This function waits on a condition variable either until
536 * awakened by a signal or broadcast; or until the time
537 * specified by abstime passes.
538 *
539 * NOTES:
540 * 1) The function must be called with 'mutex' LOCKED
541 * by the calling thread, or undefined behaviour
542 * will result.
543 *
544 * 2) This routine atomically releases 'mutex' and causes
545 * the calling thread to block on the condition variable.
546 * The blocked thread may be awakened by
547 * pthread_cond_signal or
548 * pthread_cond_broadcast.
549 *
550 *
551 * RESULTS
552 * 0 caught condition; mutex released,
553 * EINVAL 'cond', 'mutex', or abstime is invalid,
554 * EINVAL different mutexes for concurrent waits,
555 * EINVAL mutex is not held by the calling thread,
556 * ETIMEDOUT abstime ellapsed before cond was signaled.
557 *
558 * ------------------------------------------------------
559 */
560 {
561 if (abstime == NULL)
562 {
563 return EINVAL;
564 }
565
566 return (pte_cond_timedwait (cond, mutex, abstime));
567
568 } /* pthread_cond_timedwait */
569