2 * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
39 #include "pthread_private.h"
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);
48 /* Reinitialize a condition variable to defaults. */
50 _cond_reinit(pthread_cond_t * cond)
56 else if (*cond == NULL)
57 ret = pthread_cond_init(cond, NULL);
60 * Initialize the condition variable structure:
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));
72 pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
74 enum pthread_cond_type type;
82 * Check if a pointer to a condition variable attribute
83 * structure was passed by the caller:
85 if (cond_attr != NULL && *cond_attr != NULL) {
86 /* Default to a fast condition variable: */
87 type = (*cond_attr)->c_type;
89 /* Default to a fast condition variable: */
90 type = COND_TYPE_FAST;
93 /* Process according to condition variable type: */
95 /* Fast condition variable: */
97 /* Nothing to do here. */
100 /* Trap invalid condition variable types: */
102 /* Return an invalid argument error: */
107 /* Check for no errors: */
109 if ((pcond = (pthread_cond_t)
110 malloc(sizeof(struct pthread_cond))) == NULL) {
114 * Initialise the condition variable
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));
126 /* Return the completion status: */
131 pthread_cond_destroy(pthread_cond_t * cond)
135 if (cond == NULL || *cond == NULL)
138 /* Lock the condition variable structure: */
139 _SPINLOCK(&(*cond)->lock);
142 * Free the memory allocated for the condition
143 * variable structure:
148 * NULL the caller's pointer now that the condition
149 * variable has been destroyed:
153 /* Return the completion status: */
158 pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
167 * If the condition variable is statically initialized,
168 * perform the dynamic initialization:
170 else if (*cond != NULL ||
171 (rval = pthread_cond_init(cond,NULL)) == 0) {
172 /* Lock the condition variable structure: */
173 _SPINLOCK(&(*cond)->lock);
176 * If the condvar was statically allocated, properly
177 * initialize the tail queue.
179 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
180 TAILQ_INIT(&(*cond)->c_queue);
181 (*cond)->c_flags |= COND_FLAGS_INITED;
184 /* Process according to condition variable type: */
185 switch ((*cond)->c_type) {
186 /* Fast condition variable: */
188 if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
189 ((*cond)->c_mutex != *mutex))) {
190 /* Unlock the condition variable structure: */
191 _SPINUNLOCK(&(*cond)->lock);
193 /* Return invalid argument error: */
196 /* Reset the timeout flag: */
197 _thread_run->timeout = 0;
200 * Queue the running thread for the condition
203 cond_queue_enq(*cond, _thread_run);
205 /* Remember the mutex that is being used: */
206 (*cond)->c_mutex = *mutex;
209 _thread_run->wakeup_time.tv_sec = -1;
211 /* Unlock the mutex: */
212 if ((rval = _mutex_cv_unlock(mutex)) != 0) {
214 * Cannot unlock the mutex, so remove
215 * the running thread from the condition
218 cond_queue_remove(*cond, _thread_run);
220 /* Check for no more waiters: */
221 if (TAILQ_FIRST(&(*cond)->c_queue) ==
223 (*cond)->c_mutex = NULL;
225 /* Unlock the condition variable structure: */
226 _SPINUNLOCK(&(*cond)->lock);
230 * Schedule the next thread and unlock
231 * the condition variable structure:
233 _thread_kern_sched_state_unlock(PS_COND_WAIT,
234 &(*cond)->lock, __FILE__, __LINE__);
236 /* Lock the mutex: */
237 rval = _mutex_cv_lock(mutex);
242 /* Trap invalid condition variable types: */
244 /* Unlock the condition variable structure: */
245 _SPINUNLOCK(&(*cond)->lock);
247 /* Return an invalid argument error: */
253 /* Return the completion status: */
258 pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
259 const struct timespec * abstime)
264 if (cond == NULL || abstime == NULL)
267 if (abstime->tv_sec < 0 ||
268 abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
274 * If the condition variable is statically initialized,
275 * perform the dynamic initialization:
278 (rval = pthread_cond_init(cond,NULL)) == 0) {
279 /* Lock the condition variable structure: */
280 _SPINLOCK(&(*cond)->lock);
283 * If the condvar was statically allocated, properly
284 * initialize the tail queue.
286 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
287 TAILQ_INIT(&(*cond)->c_queue);
288 (*cond)->c_flags |= COND_FLAGS_INITED;
291 /* Process according to condition variable type: */
292 switch ((*cond)->c_type) {
293 /* Fast condition variable: */
295 if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
296 ((*cond)->c_mutex != *mutex))) {
297 /* Return invalid argument error: */
300 /* Unlock the condition variable structure: */
301 _SPINUNLOCK(&(*cond)->lock);
303 /* Set the wakeup time: */
304 _thread_run->wakeup_time.tv_sec =
306 _thread_run->wakeup_time.tv_nsec =
309 /* Reset the timeout flag: */
310 _thread_run->timeout = 0;
313 * Queue the running thread for the condition
316 cond_queue_enq(*cond, _thread_run);
318 /* Remember the mutex that is being used: */
319 (*cond)->c_mutex = *mutex;
321 /* Unlock the mutex: */
322 if ((rval = _mutex_cv_unlock(mutex)) != 0) {
324 * Cannot unlock the mutex, so remove
325 * the running thread from the condition
328 cond_queue_remove(*cond, _thread_run);
330 /* Check for no more waiters: */
331 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
332 (*cond)->c_mutex = NULL;
334 /* Unlock the condition variable structure: */
335 _SPINUNLOCK(&(*cond)->lock);
338 * Schedule the next thread and unlock
339 * the condition variable structure:
341 _thread_kern_sched_state_unlock(PS_COND_WAIT,
342 &(*cond)->lock, __FILE__, __LINE__);
344 /* Check if the wait timedout: */
345 if (_thread_run->timeout == 0) {
346 /* Lock the mutex: */
347 rval = _mutex_cv_lock(mutex);
350 /* Lock the condition variable structure: */
351 _SPINLOCK(&(*cond)->lock);
354 * The wait timed out; remove
355 * the thread from the condition
358 cond_queue_remove(*cond,
361 /* Check for no more waiters: */
362 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
363 (*cond)->c_mutex = NULL;
365 /* Unock the condition variable structure: */
366 _SPINUNLOCK(&(*cond)->lock);
368 /* Return a timeout error: */
372 * Lock the mutex and ignore
375 (void)_mutex_cv_lock(mutex);
381 /* Trap invalid condition variable types: */
383 /* Unlock the condition variable structure: */
384 _SPINUNLOCK(&(*cond)->lock);
386 /* Return an invalid argument error: */
393 /* Return the completion status: */
398 pthread_cond_signal(pthread_cond_t * cond)
403 if (cond == NULL || *cond == NULL)
407 * Defer signals to protect the scheduling queues
408 * from access by the signal handler:
410 _thread_kern_sig_defer();
412 /* Lock the condition variable structure: */
413 _SPINLOCK(&(*cond)->lock);
415 /* Process according to condition variable type: */
416 switch ((*cond)->c_type) {
417 /* Fast condition variable: */
420 * Enter a loop to dequeue threads from the condition
421 * queue until we find one that hasn't previously
424 while (((pthread = cond_queue_deq(*cond)) != NULL) &&
425 (pthread->timeout != 0)) {
429 /* Allow the thread to run: */
430 PTHREAD_NEW_STATE(pthread,PS_RUNNING);
432 /* Check for no more waiters: */
433 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
434 (*cond)->c_mutex = NULL;
437 /* Trap invalid condition variable types: */
439 /* Return an invalid argument error: */
444 /* Unlock the condition variable structure: */
445 _SPINUNLOCK(&(*cond)->lock);
448 * Undefer and handle pending signals, yielding if
451 _thread_kern_sig_undefer();
454 /* Return the completion status: */
459 pthread_cond_broadcast(pthread_cond_t * cond)
464 if (cond == NULL || *cond == NULL)
468 * Defer signals to protect the scheduling queues
469 * from access by the signal handler:
471 _thread_kern_sig_defer();
473 /* Lock the condition variable structure: */
474 _SPINLOCK(&(*cond)->lock);
476 /* Process according to condition variable type: */
477 switch ((*cond)->c_type) {
478 /* Fast condition variable: */
481 * Enter a loop to bring all threads off the
484 while ((pthread = cond_queue_deq(*cond)) != NULL) {
486 * The thread is already running if the
487 * timeout flag is set.
489 if (pthread->timeout == 0)
490 PTHREAD_NEW_STATE(pthread,PS_RUNNING);
493 /* There are no more waiting threads: */
494 (*cond)->c_mutex = NULL;
497 /* Trap invalid condition variable types: */
499 /* Return an invalid argument error: */
504 /* Unlock the condition variable structure: */
505 _SPINUNLOCK(&(*cond)->lock);
508 * Undefer and handle pending signals, yielding if
511 _thread_kern_sig_undefer();
514 /* Return the completion status: */
519 * Dequeue a waiting thread from the head of a condition queue in
520 * descending priority order.
522 static inline pthread_t
523 cond_queue_deq(pthread_cond_t cond)
527 if ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
528 TAILQ_REMOVE(&cond->c_queue, pthread, qe);
529 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
536 * Remove a waiting thread from a condition queue in descending priority
540 cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
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.
548 if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) {
549 TAILQ_REMOVE(&cond->c_queue, pthread, qe);
550 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
555 * Enqueue a waiting thread to a condition queue in descending priority
559 cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
561 pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
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.
568 if ((tid == NULL) || (pthread->active_priority <= tid->active_priority))
569 TAILQ_INSERT_TAIL(&cond->c_queue, pthread, qe);
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);
576 pthread->flags |= PTHREAD_FLAGS_IN_CONDQ;