xref: /relibc/pthreads-emb/pthread_cond_wait.c (revision 062c5bc4dfeed2c1bed58ed4810dd27adb32c68d)
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