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
32 * $FreeBSD: src/lib/libc_r/uthread/uthread_cond.c,v 1.22.2.8 2002/10/22 14:44:02 fjoe Exp $
38 #include "pthread_private.h"
43 static inline pthread_t cond_queue_deq(pthread_cond_t);
44 static inline void cond_queue_remove(pthread_cond_t, pthread_t);
45 static inline void cond_queue_enq(pthread_cond_t, pthread_t);
47 __weak_reference(_pthread_cond_init, pthread_cond_init);
48 __weak_reference(_pthread_cond_destroy, pthread_cond_destroy);
49 __weak_reference(_pthread_cond_wait, pthread_cond_wait);
50 __weak_reference(_pthread_cond_timedwait, pthread_cond_timedwait);
51 __weak_reference(_pthread_cond_signal, pthread_cond_signal);
52 __weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
55 /* Reinitialize a condition variable to defaults. */
57 _cond_reinit(pthread_cond_t *cond)
63 else if (*cond == NULL)
64 ret = pthread_cond_init(cond, NULL);
67 * Initialize the condition variable structure:
69 TAILQ_INIT(&(*cond)->c_queue);
70 (*cond)->c_flags = COND_FLAGS_INITED;
71 (*cond)->c_type = COND_TYPE_FAST;
72 (*cond)->c_mutex = NULL;
74 memset(&(*cond)->lock, 0, sizeof((*cond)->lock));
80 _pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
82 enum pthread_cond_type type;
90 * Check if a pointer to a condition variable attribute
91 * structure was passed by the caller:
93 if (cond_attr != NULL && *cond_attr != NULL) {
94 /* Default to a fast condition variable: */
95 type = (*cond_attr)->c_type;
97 /* Default to a fast condition variable: */
98 type = COND_TYPE_FAST;
101 /* Process according to condition variable type: */
103 /* Fast condition variable: */
105 /* Nothing to do here. */
108 /* Trap invalid condition variable types: */
110 /* Return an invalid argument error: */
115 /* Check for no errors: */
117 if ((pcond = (pthread_cond_t)
118 malloc(sizeof(struct pthread_cond))) == NULL) {
122 * Initialise the condition variable
125 TAILQ_INIT(&pcond->c_queue);
126 pcond->c_flags |= COND_FLAGS_INITED;
127 pcond->c_type = type;
128 pcond->c_mutex = NULL;
130 memset(&pcond->lock,0,sizeof(pcond->lock));
135 /* Return the completion status: */
140 _pthread_cond_destroy(pthread_cond_t *cond)
144 if (cond == NULL || *cond == NULL)
147 /* Lock the condition variable structure: */
148 _SPINLOCK(&(*cond)->lock);
151 * Free the memory allocated for the condition
152 * variable structure:
157 * NULL the caller's pointer now that the condition
158 * variable has been destroyed:
162 /* Return the completion status: */
167 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
169 struct pthread *curthread = _get_curthread();
175 _thread_enter_cancellation_point();
181 * If the condition variable is statically initialized,
182 * perform the dynamic initialization:
185 (rval = pthread_cond_init(cond, NULL)) != 0)
189 * Enter a loop waiting for a condition signal or broadcast
190 * to wake up this thread. A loop is needed in case the waiting
191 * thread is interrupted by a signal to execute a signal handler.
192 * It is not (currently) possible to remain in the waiting queue
193 * while running a handler. Instead, the thread is interrupted
194 * and backed out of the waiting queue prior to executing the
198 /* Lock the condition variable structure: */
199 _SPINLOCK(&(*cond)->lock);
202 * If the condvar was statically allocated, properly
203 * initialize the tail queue.
205 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
206 TAILQ_INIT(&(*cond)->c_queue);
207 (*cond)->c_flags |= COND_FLAGS_INITED;
210 /* Process according to condition variable type: */
211 switch ((*cond)->c_type) {
212 /* Fast condition variable: */
214 if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
215 ((*cond)->c_mutex != *mutex))) {
216 /* Unlock the condition variable structure: */
217 _SPINUNLOCK(&(*cond)->lock);
219 /* Return invalid argument error: */
222 /* Reset the timeout and interrupted flags: */
223 curthread->timeout = 0;
224 curthread->interrupted = 0;
227 * Queue the running thread for the condition
230 cond_queue_enq(*cond, curthread);
232 /* Remember the mutex and sequence number: */
233 (*cond)->c_mutex = *mutex;
234 seqno = (*cond)->c_seqno;
237 curthread->wakeup_time.tv_sec = -1;
239 /* Unlock the mutex: */
240 if ((rval = _mutex_cv_unlock(mutex)) != 0) {
242 * Cannot unlock the mutex, so remove
243 * the running thread from the condition
246 cond_queue_remove(*cond, curthread);
248 /* Check for no more waiters: */
249 if (TAILQ_FIRST(&(*cond)->c_queue) ==
251 (*cond)->c_mutex = NULL;
253 /* Unlock the condition variable structure: */
254 _SPINUNLOCK(&(*cond)->lock);
257 * Schedule the next thread and unlock
258 * the condition variable structure:
260 _thread_kern_sched_state_unlock(PS_COND_WAIT,
261 &(*cond)->lock, __FILE__, __LINE__);
263 done = (seqno != (*cond)->c_seqno);
265 interrupted = curthread->interrupted;
268 * Check if the wait was interrupted
269 * (canceled) or needs to be resumed
270 * after handling a signal.
272 if (interrupted != 0) {
274 * Lock the mutex and ignore any
275 * errors. Note that even
276 * though this thread may have
277 * been canceled, POSIX requires
278 * that the mutex be reaquired
279 * prior to cancellation.
281 (void)_mutex_cv_lock(mutex);
284 * Lock the condition variable
285 * while removing the thread.
287 _SPINLOCK(&(*cond)->lock);
289 cond_queue_remove(*cond,
292 /* Check for no more waiters: */
293 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
294 (*cond)->c_mutex = NULL;
296 _SPINUNLOCK(&(*cond)->lock);
298 /* Lock the mutex: */
299 rval = _mutex_cv_lock(mutex);
305 /* Trap invalid condition variable types: */
307 /* Unlock the condition variable structure: */
308 _SPINUNLOCK(&(*cond)->lock);
310 /* Return an invalid argument error: */
315 if ((interrupted != 0) && (curthread->continuation != NULL))
316 curthread->continuation((void *) curthread);
317 } while ((done == 0) && (rval == 0));
319 _thread_leave_cancellation_point();
321 /* Return the completion status: */
326 _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
327 const struct timespec * abstime)
329 struct pthread *curthread = _get_curthread();
335 _thread_enter_cancellation_point();
337 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
338 abstime->tv_nsec >= 1000000000)
341 * If the condition variable is statically initialized, perform dynamic
344 if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0)
348 * Enter a loop waiting for a condition signal or broadcast
349 * to wake up this thread. A loop is needed in case the waiting
350 * thread is interrupted by a signal to execute a signal handler.
351 * It is not (currently) possible to remain in the waiting queue
352 * while running a handler. Instead, the thread is interrupted
353 * and backed out of the waiting queue prior to executing the
357 /* Lock the condition variable structure: */
358 _SPINLOCK(&(*cond)->lock);
361 * If the condvar was statically allocated, properly
362 * initialize the tail queue.
364 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
365 TAILQ_INIT(&(*cond)->c_queue);
366 (*cond)->c_flags |= COND_FLAGS_INITED;
369 /* Process according to condition variable type: */
370 switch ((*cond)->c_type) {
371 /* Fast condition variable: */
373 if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
374 ((*cond)->c_mutex != *mutex))) {
375 /* Return invalid argument error: */
378 /* Unlock the condition variable structure: */
379 _SPINUNLOCK(&(*cond)->lock);
381 /* Set the wakeup time: */
382 curthread->wakeup_time.tv_sec =
384 curthread->wakeup_time.tv_nsec =
387 /* Reset the timeout and interrupted flags: */
388 curthread->timeout = 0;
389 curthread->interrupted = 0;
392 * Queue the running thread for the condition
395 cond_queue_enq(*cond, curthread);
397 /* Remember the mutex and sequence number: */
398 (*cond)->c_mutex = *mutex;
399 seqno = (*cond)->c_seqno;
401 /* Unlock the mutex: */
402 if ((rval = _mutex_cv_unlock(mutex)) != 0) {
404 * Cannot unlock the mutex, so remove
405 * the running thread from the condition
408 cond_queue_remove(*cond, curthread);
410 /* Check for no more waiters: */
411 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
412 (*cond)->c_mutex = NULL;
414 /* Unlock the condition variable structure: */
415 _SPINUNLOCK(&(*cond)->lock);
418 * Schedule the next thread and unlock
419 * the condition variable structure:
421 _thread_kern_sched_state_unlock(PS_COND_WAIT,
422 &(*cond)->lock, __FILE__, __LINE__);
424 done = (seqno != (*cond)->c_seqno);
426 interrupted = curthread->interrupted;
429 * Check if the wait was interrupted
430 * (canceled) or needs to be resumed
431 * after handling a signal.
433 if (interrupted != 0) {
435 * Lock the mutex and ignore any
436 * errors. Note that even
437 * though this thread may have
438 * been canceled, POSIX requires
439 * that the mutex be reaquired
440 * prior to cancellation.
442 (void)_mutex_cv_lock(mutex);
445 * Lock the condition variable
446 * while removing the thread.
448 _SPINLOCK(&(*cond)->lock);
450 cond_queue_remove(*cond,
453 /* Check for no more waiters: */
454 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
455 (*cond)->c_mutex = NULL;
457 _SPINUNLOCK(&(*cond)->lock);
459 /* Lock the mutex: */
460 rval = _mutex_cv_lock(mutex);
463 * Return ETIMEDOUT if the wait
464 * timed out and there wasn't an
465 * error locking the mutex:
467 if ((curthread->timeout != 0)
475 /* Trap invalid condition variable types: */
477 /* Unlock the condition variable structure: */
478 _SPINUNLOCK(&(*cond)->lock);
480 /* Return an invalid argument error: */
485 if ((interrupted != 0) && (curthread->continuation != NULL))
486 curthread->continuation((void *) curthread);
487 } while ((done == 0) && (rval == 0));
489 _thread_leave_cancellation_point();
491 /* Return the completion status: */
496 _pthread_cond_signal(pthread_cond_t * cond)
504 * If the condition variable is statically initialized, perform dynamic
507 else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) {
509 * Defer signals to protect the scheduling queues
510 * from access by the signal handler:
512 _thread_kern_sig_defer();
514 /* Lock the condition variable structure: */
515 _SPINLOCK(&(*cond)->lock);
517 /* Process according to condition variable type: */
518 switch ((*cond)->c_type) {
519 /* Fast condition variable: */
521 /* Increment the sequence number: */
524 if ((pthread = cond_queue_deq(*cond)) != NULL) {
526 * Wake up the signaled thread:
528 PTHREAD_NEW_STATE(pthread, PS_RUNNING);
531 /* Check for no more waiters: */
532 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
533 (*cond)->c_mutex = NULL;
536 /* Trap invalid condition variable types: */
538 /* Return an invalid argument error: */
543 /* Unlock the condition variable structure: */
544 _SPINUNLOCK(&(*cond)->lock);
547 * Undefer and handle pending signals, yielding if
550 _thread_kern_sig_undefer();
553 /* Return the completion status: */
558 _pthread_cond_broadcast(pthread_cond_t * cond)
566 * If the condition variable is statically initialized, perform dynamic
569 else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) {
571 * Defer signals to protect the scheduling queues
572 * from access by the signal handler:
574 _thread_kern_sig_defer();
576 /* Lock the condition variable structure: */
577 _SPINLOCK(&(*cond)->lock);
579 /* Process according to condition variable type: */
580 switch ((*cond)->c_type) {
581 /* Fast condition variable: */
583 /* Increment the sequence number: */
587 * Enter a loop to bring all threads off the
590 while ((pthread = cond_queue_deq(*cond)) != NULL) {
592 * Wake up the signaled thread:
594 PTHREAD_NEW_STATE(pthread, PS_RUNNING);
597 /* There are no more waiting threads: */
598 (*cond)->c_mutex = NULL;
601 /* Trap invalid condition variable types: */
603 /* Return an invalid argument error: */
608 /* Unlock the condition variable structure: */
609 _SPINUNLOCK(&(*cond)->lock);
612 * Undefer and handle pending signals, yielding if
615 _thread_kern_sig_undefer();
618 /* Return the completion status: */
623 _cond_wait_backout(pthread_t pthread)
627 cond = pthread->data.cond;
630 * Defer signals to protect the scheduling queues
631 * from access by the signal handler:
633 _thread_kern_sig_defer();
635 /* Lock the condition variable structure: */
636 _SPINLOCK(&cond->lock);
638 /* Process according to condition variable type: */
639 switch (cond->c_type) {
640 /* Fast condition variable: */
642 cond_queue_remove(cond, pthread);
644 /* Check for no more waiters: */
645 if (TAILQ_FIRST(&cond->c_queue) == NULL)
646 cond->c_mutex = NULL;
653 /* Unlock the condition variable structure: */
654 _SPINUNLOCK(&cond->lock);
657 * Undefer and handle pending signals, yielding if
660 _thread_kern_sig_undefer();
665 * Dequeue a waiting thread from the head of a condition queue in
666 * descending priority order.
668 static inline pthread_t
669 cond_queue_deq(pthread_cond_t cond)
673 while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
674 TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
675 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
676 if ((pthread->timeout == 0) && (pthread->interrupted == 0))
678 * Only exit the loop when we find a thread
679 * that hasn't timed out or been canceled;
680 * those threads are already running and don't
681 * need their run state changed.
690 * Remove a waiting thread from a condition queue in descending priority
694 cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
697 * Because pthread_cond_timedwait() can timeout as well
698 * as be signaled by another thread, it is necessary to
699 * guard against removing the thread from the queue if
700 * it isn't in the queue.
702 if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) {
703 TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
704 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
709 * Enqueue a waiting thread to a condition queue in descending priority
713 cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
715 pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
717 PTHREAD_ASSERT_NOT_IN_SYNCQ(pthread);
720 * For the common case of all threads having equal priority,
721 * we perform a quick check against the priority of the thread
722 * at the tail of the queue.
724 if ((tid == NULL) || (pthread->active_priority <= tid->active_priority))
725 TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe);
727 tid = TAILQ_FIRST(&cond->c_queue);
728 while (pthread->active_priority <= tid->active_priority)
729 tid = TAILQ_NEXT(tid, sqe);
730 TAILQ_INSERT_BEFORE(tid, pthread, sqe);
732 pthread->flags |= PTHREAD_FLAGS_IN_CONDQ;
733 pthread->data.cond = cond;