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
);