]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc_r/uthread/uthread_cond.c
When checking for valid timevals in the wrapped select() and poll()
[FreeBSD/FreeBSD.git] / lib / libc_r / uthread / uthread_cond.c
1 /*
2  * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by John Birrell.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $FreeBSD$
33  */
34 #include <stdlib.h>
35 #include <errno.h>
36 #include <string.h>
37 #ifdef _THREAD_SAFE
38 #include <pthread.h>
39 #include "pthread_private.h"
40
41 /*
42  * Prototypes
43  */
44 static inline pthread_t cond_queue_deq(pthread_cond_t);
45 static inline void      cond_queue_remove(pthread_cond_t, pthread_t);
46 static inline void      cond_queue_enq(pthread_cond_t, pthread_t);
47
48 /* Reinitialize a condition variable to defaults. */
49 int
50 _cond_reinit(pthread_cond_t * cond)
51 {
52         int ret = 0;
53
54         if (cond == NULL)
55                 ret = EINVAL;
56         else if (*cond == NULL)
57                 ret = pthread_cond_init(cond, NULL);
58         else {
59                 /*
60                  * Initialize the condition variable structure:
61                  */
62                 TAILQ_INIT(&(*cond)->c_queue);
63                 (*cond)->c_flags = COND_FLAGS_INITED;
64                 (*cond)->c_type = COND_TYPE_FAST;
65                 (*cond)->c_mutex = NULL;
66                 memset(&(*cond)->lock, 0, sizeof((*cond)->lock));
67         }
68         return (ret);
69 }
70
71 int
72 pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
73 {
74         enum pthread_cond_type type;
75         pthread_cond_t  pcond;
76         int             rval = 0;
77
78         if (cond == NULL)
79                 rval = EINVAL;
80         else {
81                 /*
82                  * Check if a pointer to a condition variable attribute
83                  * structure was passed by the caller: 
84                  */
85                 if (cond_attr != NULL && *cond_attr != NULL) {
86                         /* Default to a fast condition variable: */
87                         type = (*cond_attr)->c_type;
88                 } else {
89                         /* Default to a fast condition variable: */
90                         type = COND_TYPE_FAST;
91                 }
92
93                 /* Process according to condition variable type: */
94                 switch (type) {
95                 /* Fast condition variable: */
96                 case COND_TYPE_FAST:
97                         /* Nothing to do here. */
98                         break;
99
100                 /* Trap invalid condition variable types: */
101                 default:
102                         /* Return an invalid argument error: */
103                         rval = EINVAL;
104                         break;
105                 }
106
107                 /* Check for no errors: */
108                 if (rval == 0) {
109                         if ((pcond = (pthread_cond_t)
110                             malloc(sizeof(struct pthread_cond))) == NULL) {
111                                 rval = ENOMEM;
112                         } else {
113                                 /*
114                                  * Initialise the condition variable
115                                  * structure:
116                                  */
117                                 TAILQ_INIT(&pcond->c_queue);
118                                 pcond->c_flags |= COND_FLAGS_INITED;
119                                 pcond->c_type = type;
120                                 pcond->c_mutex = NULL;
121                                 memset(&pcond->lock,0,sizeof(pcond->lock));
122                                 *cond = pcond;
123                         }
124                 }
125         }
126         /* Return the completion status: */
127         return (rval);
128 }
129
130 int
131 pthread_cond_destroy(pthread_cond_t * cond)
132 {
133         int             rval = 0;
134
135         if (cond == NULL || *cond == NULL)
136                 rval = EINVAL;
137         else {
138                 /* Lock the condition variable structure: */
139                 _SPINLOCK(&(*cond)->lock);
140
141                 /*
142                  * Free the memory allocated for the condition
143                  * variable structure:
144                  */
145                 free(*cond);
146
147                 /*
148                  * NULL the caller's pointer now that the condition
149                  * variable has been destroyed:
150                  */
151                 *cond = NULL;
152         }
153         /* Return the completion status: */
154         return (rval);
155 }
156
157 int
158 pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
159 {
160         int             rval = 0;
161         int             status;
162
163         if (cond == NULL)
164                 rval = EINVAL;
165
166         /*
167          * If the condition variable is statically initialized,
168          * perform the dynamic initialization:
169          */
170         else if (*cond != NULL ||
171             (rval = pthread_cond_init(cond,NULL)) == 0) {
172                 /* Lock the condition variable structure: */
173                 _SPINLOCK(&(*cond)->lock);
174
175                 /*
176                  * If the condvar was statically allocated, properly
177                  * initialize the tail queue.
178                  */
179                 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
180                         TAILQ_INIT(&(*cond)->c_queue);
181                         (*cond)->c_flags |= COND_FLAGS_INITED;
182                 }
183
184                 /* Process according to condition variable type: */
185                 switch ((*cond)->c_type) {
186                 /* Fast condition variable: */
187                 case COND_TYPE_FAST:
188                         if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
189                             ((*cond)->c_mutex != *mutex))) {
190                                 /* Unlock the condition variable structure: */
191                                 _SPINUNLOCK(&(*cond)->lock);
192
193                                 /* Return invalid argument error: */
194                                 rval = EINVAL;
195                         } else {
196                                 /* Reset the timeout flag: */
197                                 _thread_run->timeout = 0;
198
199                                 /*
200                                  * Queue the running thread for the condition
201                                  * variable:
202                                  */
203                                 cond_queue_enq(*cond, _thread_run);
204
205                                 /* Remember the mutex that is being used: */
206                                 (*cond)->c_mutex = *mutex;
207
208                                 /* Wait forever: */
209                                 _thread_run->wakeup_time.tv_sec = -1;
210
211                                 /* Unlock the mutex: */
212                                 if ((rval = _mutex_cv_unlock(mutex)) != 0) {
213                                         /*
214                                          * Cannot unlock the mutex, so remove
215                                          * the running thread from the condition
216                                          * variable queue:
217                                          */
218                                         cond_queue_remove(*cond, _thread_run);
219
220                                         /* Check for no more waiters: */
221                                         if (TAILQ_FIRST(&(*cond)->c_queue) ==
222                                             NULL)
223                                                 (*cond)->c_mutex = NULL;
224
225                                         /* Unlock the condition variable structure: */
226                                         _SPINUNLOCK(&(*cond)->lock);
227                                 }
228                                 else {
229                                         /*
230                                          * Schedule the next thread and unlock
231                                          * the condition variable structure:
232                                          */
233                                         _thread_kern_sched_state_unlock(PS_COND_WAIT,
234                                             &(*cond)->lock, __FILE__, __LINE__);
235
236                                         /* Lock the mutex: */
237                                         rval = _mutex_cv_lock(mutex);
238                                 }
239                         }
240                         break;
241
242                 /* Trap invalid condition variable types: */
243                 default:
244                         /* Unlock the condition variable structure: */
245                         _SPINUNLOCK(&(*cond)->lock);
246
247                         /* Return an invalid argument error: */
248                         rval = EINVAL;
249                         break;
250                 }
251         }
252
253         /* Return the completion status: */
254         return (rval);
255 }
256
257 int
258 pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
259                        const struct timespec * abstime)
260 {
261         int             rval = 0;
262         int             status;
263
264         if (cond == NULL || abstime == NULL)
265                 rval = EINVAL;
266
267         if (abstime->tv_sec < 0 || 
268                 abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
269                 errno = EINVAL;
270                 return (-1);
271         }
272
273         /*
274          * If the condition variable is statically initialized,
275          * perform the dynamic initialization:
276          */
277         if (*cond != NULL ||
278             (rval = pthread_cond_init(cond,NULL)) == 0) {
279                 /* Lock the condition variable structure: */
280                 _SPINLOCK(&(*cond)->lock);
281
282                 /*
283                  * If the condvar was statically allocated, properly
284                  * initialize the tail queue.
285                  */
286                 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
287                         TAILQ_INIT(&(*cond)->c_queue);
288                         (*cond)->c_flags |= COND_FLAGS_INITED;
289                 }
290
291                 /* Process according to condition variable type: */
292                 switch ((*cond)->c_type) {
293                 /* Fast condition variable: */
294                 case COND_TYPE_FAST:
295                         if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
296                             ((*cond)->c_mutex != *mutex))) {
297                                 /* Return invalid argument error: */
298                                 rval = EINVAL;
299
300                                 /* Unlock the condition variable structure: */
301                                 _SPINUNLOCK(&(*cond)->lock);
302                         } else {
303                                 /* Set the wakeup time: */
304                                 _thread_run->wakeup_time.tv_sec =
305                                     abstime->tv_sec;
306                                 _thread_run->wakeup_time.tv_nsec =
307                                     abstime->tv_nsec;
308
309                                 /* Reset the timeout flag: */
310                                 _thread_run->timeout = 0;
311
312                                 /*
313                                  * Queue the running thread for the condition
314                                  * variable:
315                                  */
316                                 cond_queue_enq(*cond, _thread_run);
317
318                                 /* Remember the mutex that is being used: */
319                                 (*cond)->c_mutex = *mutex;
320
321                                 /* Unlock the mutex: */
322                                 if ((rval = _mutex_cv_unlock(mutex)) != 0) {
323                                         /*
324                                          * Cannot unlock the mutex, so remove
325                                          * the running thread from the condition
326                                          * variable queue: 
327                                          */
328                                         cond_queue_remove(*cond, _thread_run);
329
330                                         /* Check for no more waiters: */
331                                         if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
332                                                 (*cond)->c_mutex = NULL;
333
334                                         /* Unlock the condition variable structure: */
335                                         _SPINUNLOCK(&(*cond)->lock);
336                                 } else {
337                                         /*
338                                          * Schedule the next thread and unlock
339                                          * the condition variable structure:
340                                          */
341                                         _thread_kern_sched_state_unlock(PS_COND_WAIT,
342                                              &(*cond)->lock, __FILE__, __LINE__);
343
344                                         /* Check if the wait timedout: */
345                                         if (_thread_run->timeout == 0) {
346                                                 /* Lock the mutex: */
347                                                 rval = _mutex_cv_lock(mutex);
348                                         }
349                                         else {
350                                                 /* Lock the condition variable structure: */
351                                                 _SPINLOCK(&(*cond)->lock);
352
353                                                 /*
354                                                  * The wait timed out; remove
355                                                  * the thread from the condition
356                                                  * variable queue:
357                                                  */
358                                                 cond_queue_remove(*cond,
359                                                     _thread_run);
360
361                                                 /* Check for no more waiters: */
362                                                 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
363                                                         (*cond)->c_mutex = NULL;
364
365                                                 /* Unock the condition variable structure: */
366                                                 _SPINUNLOCK(&(*cond)->lock);
367
368                                                 /* Return a timeout error: */
369                                                 rval = ETIMEDOUT;
370
371                                                 /*
372                                                  * Lock the mutex and ignore
373                                                  * any errors:
374                                                  */
375                                                 (void)_mutex_cv_lock(mutex);
376                                         }
377                                 }
378                         }
379                         break;
380
381                 /* Trap invalid condition variable types: */
382                 default:
383                         /* Unlock the condition variable structure: */
384                         _SPINUNLOCK(&(*cond)->lock);
385
386                         /* Return an invalid argument error: */
387                         rval = EINVAL;
388                         break;
389                 }
390
391         }
392
393         /* Return the completion status: */
394         return (rval);
395 }
396
397 int
398 pthread_cond_signal(pthread_cond_t * cond)
399 {
400         int             rval = 0;
401         pthread_t       pthread;
402
403         if (cond == NULL || *cond == NULL)
404                 rval = EINVAL;
405         else {
406                 /*
407                  * Defer signals to protect the scheduling queues
408                  * from access by the signal handler:
409                  */
410                 _thread_kern_sig_defer();
411
412                 /* Lock the condition variable structure: */
413                 _SPINLOCK(&(*cond)->lock);
414
415                 /* Process according to condition variable type: */
416                 switch ((*cond)->c_type) {
417                 /* Fast condition variable: */
418                 case COND_TYPE_FAST:
419                         /*
420                          * Enter a loop to dequeue threads from the condition
421                          * queue until we find one that hasn't previously
422                          * timed out.
423                          */
424                         while (((pthread = cond_queue_deq(*cond)) != NULL) &&
425                             (pthread->timeout != 0)) {
426                         }
427
428                         if (pthread != NULL)
429                                 /* Allow the thread to run: */
430                                 PTHREAD_NEW_STATE(pthread,PS_RUNNING);
431
432                         /* Check for no more waiters: */
433                         if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
434                                 (*cond)->c_mutex = NULL;
435                         break;
436
437                 /* Trap invalid condition variable types: */
438                 default:
439                         /* Return an invalid argument error: */
440                         rval = EINVAL;
441                         break;
442                 }
443
444                 /* Unlock the condition variable structure: */
445                 _SPINUNLOCK(&(*cond)->lock);
446
447                 /*
448                  * Undefer and handle pending signals, yielding if
449                  * necessary:
450                  */
451                 _thread_kern_sig_undefer();
452         }
453
454         /* Return the completion status: */
455         return (rval);
456 }
457
458 int
459 pthread_cond_broadcast(pthread_cond_t * cond)
460 {
461         int             rval = 0;
462         pthread_t       pthread;
463
464         if (cond == NULL || *cond == NULL)
465                 rval = EINVAL;
466         else {
467                 /*
468                  * Defer signals to protect the scheduling queues
469                  * from access by the signal handler:
470                  */
471                 _thread_kern_sig_defer();
472
473                 /* Lock the condition variable structure: */
474                 _SPINLOCK(&(*cond)->lock);
475
476                 /* Process according to condition variable type: */
477                 switch ((*cond)->c_type) {
478                 /* Fast condition variable: */
479                 case COND_TYPE_FAST:
480                         /*
481                          * Enter a loop to bring all threads off the
482                          * condition queue:
483                          */
484                         while ((pthread = cond_queue_deq(*cond)) != NULL) {
485                                 /*
486                                  * The thread is already running if the
487                                  * timeout flag is set.
488                                  */
489                                 if (pthread->timeout == 0)
490                                         PTHREAD_NEW_STATE(pthread,PS_RUNNING);
491                         }
492
493                         /* There are no more waiting threads: */
494                         (*cond)->c_mutex = NULL;
495                         break;
496         
497                 /* Trap invalid condition variable types: */
498                 default:
499                         /* Return an invalid argument error: */
500                         rval = EINVAL;
501                         break;
502                 }
503
504                 /* Unlock the condition variable structure: */
505                 _SPINUNLOCK(&(*cond)->lock);
506
507                 /*
508                  * Undefer and handle pending signals, yielding if
509                  * necessary:
510                  */
511                 _thread_kern_sig_undefer();
512         }
513
514         /* Return the completion status: */
515         return (rval);
516 }
517
518 /*
519  * Dequeue a waiting thread from the head of a condition queue in
520  * descending priority order.
521  */
522 static inline pthread_t
523 cond_queue_deq(pthread_cond_t cond)
524 {
525         pthread_t pthread;
526
527         if ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
528                 TAILQ_REMOVE(&cond->c_queue, pthread, qe);
529                 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
530         }
531
532         return(pthread);
533 }
534
535 /*
536  * Remove a waiting thread from a condition queue in descending priority
537  * order.
538  */
539 static inline void
540 cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
541 {
542         /*
543          * Because pthread_cond_timedwait() can timeout as well
544          * as be signaled by another thread, it is necessary to
545          * guard against removing the thread from the queue if
546          * it isn't in the queue.
547          */
548         if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) {
549                 TAILQ_REMOVE(&cond->c_queue, pthread, qe);
550                 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
551         }
552 }
553
554 /*
555  * Enqueue a waiting thread to a condition queue in descending priority
556  * order.
557  */
558 static inline void
559 cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
560 {
561         pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
562
563         /*
564          * For the common case of all threads having equal priority,
565          * we perform a quick check against the priority of the thread
566          * at the tail of the queue.
567          */
568         if ((tid == NULL) || (pthread->active_priority <= tid->active_priority))
569                 TAILQ_INSERT_TAIL(&cond->c_queue, pthread, qe);
570         else {
571                 tid = TAILQ_FIRST(&cond->c_queue);
572                 while (pthread->active_priority <= tid->active_priority)
573                         tid = TAILQ_NEXT(tid, qe);
574                 TAILQ_INSERT_BEFORE(tid, pthread, qe);
575         }
576         pthread->flags |= PTHREAD_FLAGS_IN_CONDQ;
577 }
578 #endif