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 $
33 * $DragonFly: src/lib/libc_r/uthread/uthread_cond.c,v 1.3 2005/05/30 20:50:53 joerg Exp $
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);
49 /* Reinitialize a condition variable to defaults. */
51 _cond_reinit(pthread_cond_t *cond)
57 else if (*cond == NULL)
58 ret = pthread_cond_init(cond, NULL);
61 * Initialize the condition variable structure:
63 TAILQ_INIT(&(*cond)->c_queue);
64 (*cond)->c_flags = COND_FLAGS_INITED;
65 (*cond)->c_type = COND_TYPE_FAST;
66 (*cond)->c_mutex = NULL;
68 memset(&(*cond)->lock, 0, sizeof((*cond)->lock));
74 _pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
76 enum pthread_cond_type type;
84 * Check if a pointer to a condition variable attribute
85 * structure was passed by the caller:
87 if (cond_attr != NULL && *cond_attr != NULL) {
88 /* Default to a fast condition variable: */
89 type = (*cond_attr)->c_type;
91 /* Default to a fast condition variable: */
92 type = COND_TYPE_FAST;
95 /* Process according to condition variable type: */
97 /* Fast condition variable: */
99 /* Nothing to do here. */
102 /* Trap invalid condition variable types: */
104 /* Return an invalid argument error: */
109 /* Check for no errors: */
111 if ((pcond = (pthread_cond_t)
112 malloc(sizeof(struct pthread_cond))) == NULL) {
116 * Initialise the condition variable
119 TAILQ_INIT(&pcond->c_queue);
120 pcond->c_flags |= COND_FLAGS_INITED;
121 pcond->c_type = type;
122 pcond->c_mutex = NULL;
124 memset(&pcond->lock,0,sizeof(pcond->lock));
129 /* Return the completion status: */
134 _pthread_cond_destroy(pthread_cond_t *cond)
138 if (cond == NULL || *cond == NULL)
141 /* Lock the condition variable structure: */
142 _SPINLOCK(&(*cond)->lock);
145 * Free the memory allocated for the condition
146 * variable structure:
151 * NULL the caller's pointer now that the condition
152 * variable has been destroyed:
156 /* Return the completion status: */
161 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
163 struct pthread *curthread = _get_curthread();
169 _thread_enter_cancellation_point();
175 * If the condition variable is statically initialized,
176 * perform the dynamic initialization:
179 (rval = pthread_cond_init(cond, NULL)) != 0)
183 * Enter a loop waiting for a condition signal or broadcast
184 * to wake up this thread. A loop is needed in case the waiting
185 * thread is interrupted by a signal to execute a signal handler.
186 * It is not (currently) possible to remain in the waiting queue
187 * while running a handler. Instead, the thread is interrupted
188 * and backed out of the waiting queue prior to executing the
192 /* Lock the condition variable structure: */
193 _SPINLOCK(&(*cond)->lock);
196 * If the condvar was statically allocated, properly
197 * initialize the tail queue.
199 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
200 TAILQ_INIT(&(*cond)->c_queue);
201 (*cond)->c_flags |= COND_FLAGS_INITED;
204 /* Process according to condition variable type: */
205 switch ((*cond)->c_type) {
206 /* Fast condition variable: */
208 if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
209 ((*cond)->c_mutex != *mutex))) {
210 /* Unlock the condition variable structure: */
211 _SPINUNLOCK(&(*cond)->lock);
213 /* Return invalid argument error: */
216 /* Reset the timeout and interrupted flags: */
217 curthread->timeout = 0;
218 curthread->interrupted = 0;
221 * Queue the running thread for the condition
224 cond_queue_enq(*cond, curthread);
226 /* Remember the mutex and sequence number: */
227 (*cond)->c_mutex = *mutex;
228 seqno = (*cond)->c_seqno;
231 curthread->wakeup_time.tv_sec = -1;
233 /* Unlock the mutex: */
234 if ((rval = _mutex_cv_unlock(mutex)) != 0) {
236 * Cannot unlock the mutex, so remove
237 * the running thread from the condition
240 cond_queue_remove(*cond, curthread);
242 /* Check for no more waiters: */
243 if (TAILQ_FIRST(&(*cond)->c_queue) ==
245 (*cond)->c_mutex = NULL;
247 /* Unlock the condition variable structure: */
248 _SPINUNLOCK(&(*cond)->lock);
251 * Schedule the next thread and unlock
252 * the condition variable structure:
254 _thread_kern_sched_state_unlock(PS_COND_WAIT,
255 &(*cond)->lock, __FILE__, __LINE__);
257 done = (seqno != (*cond)->c_seqno);
259 interrupted = curthread->interrupted;
262 * Check if the wait was interrupted
263 * (canceled) or needs to be resumed
264 * after handling a signal.
266 if (interrupted != 0) {
268 * Lock the mutex and ignore any
269 * errors. Note that even
270 * though this thread may have
271 * been canceled, POSIX requires
272 * that the mutex be reacquired
273 * prior to cancellation.
275 (void)_mutex_cv_lock(mutex);
278 * Lock the condition variable
279 * while removing the thread.
281 _SPINLOCK(&(*cond)->lock);
283 cond_queue_remove(*cond,
286 /* Check for no more waiters: */
287 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
288 (*cond)->c_mutex = NULL;
290 _SPINUNLOCK(&(*cond)->lock);
292 /* Lock the mutex: */
293 rval = _mutex_cv_lock(mutex);
299 /* Trap invalid condition variable types: */
301 /* Unlock the condition variable structure: */
302 _SPINUNLOCK(&(*cond)->lock);
304 /* Return an invalid argument error: */
309 if ((interrupted != 0) && (curthread->continuation != NULL))
310 curthread->continuation((void *) curthread);
311 } while ((done == 0) && (rval == 0));
313 _thread_leave_cancellation_point();
315 /* Return the completion status: */
320 _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
321 const struct timespec * abstime)
323 struct pthread *curthread = _get_curthread();
329 _thread_enter_cancellation_point();
331 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
332 abstime->tv_nsec >= 1000000000)
335 * If the condition variable is statically initialized, perform dynamic
338 if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0)
342 * Enter a loop waiting for a condition signal or broadcast
343 * to wake up this thread. A loop is needed in case the waiting
344 * thread is interrupted by a signal to execute a signal handler.
345 * It is not (currently) possible to remain in the waiting queue
346 * while running a handler. Instead, the thread is interrupted
347 * and backed out of the waiting queue prior to executing the
351 /* Lock the condition variable structure: */
352 _SPINLOCK(&(*cond)->lock);
355 * If the condvar was statically allocated, properly
356 * initialize the tail queue.
358 if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
359 TAILQ_INIT(&(*cond)->c_queue);
360 (*cond)->c_flags |= COND_FLAGS_INITED;
363 /* Process according to condition variable type: */
364 switch ((*cond)->c_type) {
365 /* Fast condition variable: */
367 if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
368 ((*cond)->c_mutex != *mutex))) {
369 /* Return invalid argument error: */
372 /* Unlock the condition variable structure: */
373 _SPINUNLOCK(&(*cond)->lock);
375 /* Set the wakeup time: */
376 curthread->wakeup_time.tv_sec =
378 curthread->wakeup_time.tv_nsec =
381 /* Reset the timeout and interrupted flags: */
382 curthread->timeout = 0;
383 curthread->interrupted = 0;
386 * Queue the running thread for the condition
389 cond_queue_enq(*cond, curthread);
391 /* Remember the mutex and sequence number: */
392 (*cond)->c_mutex = *mutex;
393 seqno = (*cond)->c_seqno;
395 /* Unlock the mutex: */
396 if ((rval = _mutex_cv_unlock(mutex)) != 0) {
398 * Cannot unlock the mutex, so remove
399 * the running thread from the condition
402 cond_queue_remove(*cond, curthread);
404 /* Check for no more waiters: */
405 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
406 (*cond)->c_mutex = NULL;
408 /* Unlock the condition variable structure: */
409 _SPINUNLOCK(&(*cond)->lock);
412 * Schedule the next thread and unlock
413 * the condition variable structure:
415 _thread_kern_sched_state_unlock(PS_COND_WAIT,
416 &(*cond)->lock, __FILE__, __LINE__);
418 done = (seqno != (*cond)->c_seqno);
420 interrupted = curthread->interrupted;
423 * Check if the wait was interrupted
424 * (canceled) or needs to be resumed
425 * after handling a signal.
427 if (interrupted != 0) {
429 * Lock the mutex and ignore any
430 * errors. Note that even
431 * though this thread may have
432 * been canceled, POSIX requires
433 * that the mutex be reacquired
434 * prior to cancellation.
436 (void)_mutex_cv_lock(mutex);
439 * Lock the condition variable
440 * while removing the thread.
442 _SPINLOCK(&(*cond)->lock);
444 cond_queue_remove(*cond,
447 /* Check for no more waiters: */
448 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
449 (*cond)->c_mutex = NULL;
451 _SPINUNLOCK(&(*cond)->lock);
453 /* Lock the mutex: */
454 rval = _mutex_cv_lock(mutex);
457 * Return ETIMEDOUT if the wait
458 * timed out and there wasn't an
459 * error locking the mutex:
461 if ((curthread->timeout != 0)
469 /* Trap invalid condition variable types: */
471 /* Unlock the condition variable structure: */
472 _SPINUNLOCK(&(*cond)->lock);
474 /* Return an invalid argument error: */
479 if ((interrupted != 0) && (curthread->continuation != NULL))
480 curthread->continuation((void *) curthread);
481 } while ((done == 0) && (rval == 0));
483 _thread_leave_cancellation_point();
485 /* Return the completion status: */
490 _pthread_cond_signal(pthread_cond_t * cond)
498 * If the condition variable is statically initialized, perform dynamic
501 else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) {
503 * Defer signals to protect the scheduling queues
504 * from access by the signal handler:
506 _thread_kern_sig_defer();
508 /* Lock the condition variable structure: */
509 _SPINLOCK(&(*cond)->lock);
511 /* Process according to condition variable type: */
512 switch ((*cond)->c_type) {
513 /* Fast condition variable: */
515 /* Increment the sequence number: */
518 if ((pthread = cond_queue_deq(*cond)) != NULL) {
520 * Wake up the signaled thread:
522 PTHREAD_NEW_STATE(pthread, PS_RUNNING);
525 /* Check for no more waiters: */
526 if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
527 (*cond)->c_mutex = NULL;
530 /* Trap invalid condition variable types: */
532 /* Return an invalid argument error: */
537 /* Unlock the condition variable structure: */
538 _SPINUNLOCK(&(*cond)->lock);
541 * Undefer and handle pending signals, yielding if
544 _thread_kern_sig_undefer();
547 /* Return the completion status: */
552 _pthread_cond_broadcast(pthread_cond_t * cond)
560 * If the condition variable is statically initialized, perform dynamic
563 else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) {
565 * Defer signals to protect the scheduling queues
566 * from access by the signal handler:
568 _thread_kern_sig_defer();
570 /* Lock the condition variable structure: */
571 _SPINLOCK(&(*cond)->lock);
573 /* Process according to condition variable type: */
574 switch ((*cond)->c_type) {
575 /* Fast condition variable: */
577 /* Increment the sequence number: */
581 * Enter a loop to bring all threads off the
584 while ((pthread = cond_queue_deq(*cond)) != NULL) {
586 * Wake up the signaled thread:
588 PTHREAD_NEW_STATE(pthread, PS_RUNNING);
591 /* There are no more waiting threads: */
592 (*cond)->c_mutex = NULL;
595 /* Trap invalid condition variable types: */
597 /* Return an invalid argument error: */
602 /* Unlock the condition variable structure: */
603 _SPINUNLOCK(&(*cond)->lock);
606 * Undefer and handle pending signals, yielding if
609 _thread_kern_sig_undefer();
612 /* Return the completion status: */
617 _cond_wait_backout(pthread_t pthread)
621 cond = pthread->data.cond;
624 * Defer signals to protect the scheduling queues
625 * from access by the signal handler:
627 _thread_kern_sig_defer();
629 /* Lock the condition variable structure: */
630 _SPINLOCK(&cond->lock);
632 /* Process according to condition variable type: */
633 switch (cond->c_type) {
634 /* Fast condition variable: */
636 cond_queue_remove(cond, pthread);
638 /* Check for no more waiters: */
639 if (TAILQ_FIRST(&cond->c_queue) == NULL)
640 cond->c_mutex = NULL;
647 /* Unlock the condition variable structure: */
648 _SPINUNLOCK(&cond->lock);
651 * Undefer and handle pending signals, yielding if
654 _thread_kern_sig_undefer();
659 * Dequeue a waiting thread from the head of a condition queue in
660 * descending priority order.
662 static inline pthread_t
663 cond_queue_deq(pthread_cond_t cond)
667 while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
668 TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
669 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
670 if ((pthread->timeout == 0) && (pthread->interrupted == 0))
672 * Only exit the loop when we find a thread
673 * that hasn't timed out or been canceled;
674 * those threads are already running and don't
675 * need their run state changed.
684 * Remove a waiting thread from a condition queue in descending priority
688 cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
691 * Because pthread_cond_timedwait() can timeout as well
692 * as be signaled by another thread, it is necessary to
693 * guard against removing the thread from the queue if
694 * it isn't in the queue.
696 if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) {
697 TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
698 pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
703 * Enqueue a waiting thread to a condition queue in descending priority
707 cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
709 pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
711 PTHREAD_ASSERT_NOT_IN_SYNCQ(pthread);
714 * For the common case of all threads having equal priority,
715 * we perform a quick check against the priority of the thread
716 * at the tail of the queue.
718 if ((tid == NULL) || (pthread->active_priority <= tid->active_priority))
719 TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe);
721 tid = TAILQ_FIRST(&cond->c_queue);
722 while (pthread->active_priority <= tid->active_priority)
723 tid = TAILQ_NEXT(tid, sqe);
724 TAILQ_INSERT_BEFORE(tid, pthread, sqe);
726 pthread->flags |= PTHREAD_FLAGS_IN_CONDQ;
727 pthread->data.cond = cond;
730 __strong_reference(_pthread_cond_init, pthread_cond_init);
731 __strong_reference(_pthread_cond_destroy, pthread_cond_destroy);
732 __strong_reference(_pthread_cond_wait, pthread_cond_wait);
733 __strong_reference(_pthread_cond_timedwait, pthread_cond_timedwait);
734 __strong_reference(_pthread_cond_signal, pthread_cond_signal);
735 __strong_reference(_pthread_cond_broadcast, pthread_cond_broadcast);