thr_cond.c revision 120069
113546Sjulian/*
213546Sjulian * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
313546Sjulian * All rights reserved.
413546Sjulian *
513546Sjulian * Redistribution and use in source and binary forms, with or without
613546Sjulian * modification, are permitted provided that the following conditions
713546Sjulian * are met:
813546Sjulian * 1. Redistributions of source code must retain the above copyright
913546Sjulian *    notice, this list of conditions and the following disclaimer.
1013546Sjulian * 2. Redistributions in binary form must reproduce the above copyright
1113546Sjulian *    notice, this list of conditions and the following disclaimer in the
1213546Sjulian *    documentation and/or other materials provided with the distribution.
1313546Sjulian * 3. All advertising materials mentioning features or use of this software
1413546Sjulian *    must display the following acknowledgement:
1513546Sjulian *	This product includes software developed by John Birrell.
1613546Sjulian * 4. Neither the name of the author nor the names of any co-contributors
1713546Sjulian *    may be used to endorse or promote products derived from this software
1813546Sjulian *    without specific prior written permission.
1913546Sjulian *
2013546Sjulian * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
2113546Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2213546Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2344963Sjb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2413546Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2513546Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2613546Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2713546Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2813546Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2913546Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3013546Sjulian * SUCH DAMAGE.
3113546Sjulian *
3250476Speter * $FreeBSD: head/lib/libkse/thread/thr_cond.c 120069 2003-09-14 22:33:32Z davidxu $
3313546Sjulian */
3413546Sjulian#include <stdlib.h>
3513546Sjulian#include <errno.h>
3636830Sjb#include <string.h>
3713546Sjulian#include <pthread.h>
38103388Smini#include "thr_private.h"
3913546Sjulian
40113658Sdeischen#define	THR_IN_CONDQ(thr)	(((thr)->sflags & THR_FLAGS_IN_SYNCQ) != 0)
41113658Sdeischen#define	THR_CONDQ_SET(thr)	(thr)->sflags |= THR_FLAGS_IN_SYNCQ
42113658Sdeischen#define	THR_CONDQ_CLEAR(thr)	(thr)->sflags &= ~THR_FLAGS_IN_SYNCQ
43113658Sdeischen
4444963Sjb/*
4544963Sjb * Prototypes
4644963Sjb */
47113658Sdeischenstatic inline struct pthread	*cond_queue_deq(pthread_cond_t);
48113658Sdeischenstatic inline void		cond_queue_remove(pthread_cond_t, pthread_t);
49113658Sdeischenstatic inline void		cond_queue_enq(pthread_cond_t, pthread_t);
5044963Sjb
51115173Sdeischen/*
52115173Sdeischen * Double underscore versions are cancellation points.  Single underscore
53115173Sdeischen * versions are not and are provided for libc internal usage (which
54115173Sdeischen * shouldn't introduce cancellation points).
55115173Sdeischen */
56115173Sdeischen__weak_reference(__pthread_cond_wait, pthread_cond_wait);
57115173Sdeischen__weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait);
58115173Sdeischen
5975369Sdeischen__weak_reference(_pthread_cond_init, pthread_cond_init);
6075369Sdeischen__weak_reference(_pthread_cond_destroy, pthread_cond_destroy);
6175369Sdeischen__weak_reference(_pthread_cond_signal, pthread_cond_signal);
6275369Sdeischen__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
6371581Sdeischen
6471581Sdeischen
6548046Sjbint
6671581Sdeischen_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
6713546Sjulian{
6813546Sjulian	enum pthread_cond_type type;
6917706Sjulian	pthread_cond_t	pcond;
70113658Sdeischen	int		flags;
7113546Sjulian	int             rval = 0;
7213546Sjulian
7335509Sjb	if (cond == NULL)
7431402Salex		rval = EINVAL;
7535509Sjb	else {
7617706Sjulian		/*
7724827Sjb		 * Check if a pointer to a condition variable attribute
7824827Sjb		 * structure was passed by the caller:
7917706Sjulian		 */
8017706Sjulian		if (cond_attr != NULL && *cond_attr != NULL) {
8117706Sjulian			/* Default to a fast condition variable: */
8217706Sjulian			type = (*cond_attr)->c_type;
83113658Sdeischen			flags = (*cond_attr)->c_flags;
8417706Sjulian		} else {
8517706Sjulian			/* Default to a fast condition variable: */
8617706Sjulian			type = COND_TYPE_FAST;
87113658Sdeischen			flags = 0;
8817706Sjulian		}
8913546Sjulian
9017706Sjulian		/* Process according to condition variable type: */
9117706Sjulian		switch (type) {
9224827Sjb		/* Fast condition variable: */
9317706Sjulian		case COND_TYPE_FAST:
9417706Sjulian			/* Nothing to do here. */
9517706Sjulian			break;
9613546Sjulian
9724827Sjb		/* Trap invalid condition variable types: */
9817706Sjulian		default:
9917706Sjulian			/* Return an invalid argument error: */
10031402Salex			rval = EINVAL;
10117706Sjulian			break;
10217706Sjulian		}
10313546Sjulian
10417706Sjulian		/* Check for no errors: */
10517706Sjulian		if (rval == 0) {
10624827Sjb			if ((pcond = (pthread_cond_t)
10724827Sjb			    malloc(sizeof(struct pthread_cond))) == NULL) {
10831402Salex				rval = ENOMEM;
109113658Sdeischen			} else if (_lock_init(&pcond->c_lock, LCK_ADAPTIVE,
110113786Sdeischen			    _thr_lock_wait, _thr_lock_wakeup) != 0) {
111113658Sdeischen				free(pcond);
112113658Sdeischen				rval = ENOMEM;
11317706Sjulian			} else {
11417706Sjulian				/*
11517706Sjulian				 * Initialise the condition variable
11617706Sjulian				 * structure:
11717706Sjulian				 */
11844963Sjb				TAILQ_INIT(&pcond->c_queue);
119120069Sdavidxu				pcond->c_flags = COND_FLAGS_INITED;
12017706Sjulian				pcond->c_type = type;
12144963Sjb				pcond->c_mutex = NULL;
12268516Sdeischen				pcond->c_seqno = 0;
12317706Sjulian				*cond = pcond;
12417706Sjulian			}
12517706Sjulian		}
12613546Sjulian	}
12713546Sjulian	/* Return the completion status: */
12813546Sjulian	return (rval);
12913546Sjulian}
13013546Sjulian
13113546Sjulianint
13271581Sdeischen_pthread_cond_destroy(pthread_cond_t *cond)
13313546Sjulian{
134113658Sdeischen	struct pthread_cond	*cv;
135113658Sdeischen	struct pthread		*curthread = _get_curthread();
136113658Sdeischen	int			rval = 0;
13713546Sjulian
13835509Sjb	if (cond == NULL || *cond == NULL)
13931402Salex		rval = EINVAL;
14035509Sjb	else {
14135509Sjb		/* Lock the condition variable structure: */
142113658Sdeischen		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
14313546Sjulian
14435509Sjb		/*
145113658Sdeischen		 * NULL the caller's pointer now that the condition
146113658Sdeischen		 * variable has been destroyed:
147113658Sdeischen		 */
148113658Sdeischen		cv = *cond;
149113658Sdeischen		*cond = NULL;
150113658Sdeischen
151113658Sdeischen		/* Unlock the condition variable structure: */
152113658Sdeischen		THR_LOCK_RELEASE(curthread, &cv->c_lock);
153113658Sdeischen
154115761Sdavidxu		/* Free the cond lock structure: */
155115761Sdavidxu		_lock_destroy(&cv->c_lock);
156115761Sdavidxu
157113658Sdeischen		/*
15835509Sjb		 * Free the memory allocated for the condition
15935509Sjb		 * variable structure:
16035509Sjb		 */
161113658Sdeischen		free(cv);
16217706Sjulian
16313546Sjulian	}
16413546Sjulian	/* Return the completion status: */
16513546Sjulian	return (rval);
16613546Sjulian}
16713546Sjulian
16813546Sjulianint
16971581Sdeischen_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
17013546Sjulian{
17171581Sdeischen	struct pthread	*curthread = _get_curthread();
17256277Sjasone	int	rval = 0;
17368516Sdeischen	int	done = 0;
17456277Sjasone	int	interrupted = 0;
175113658Sdeischen	int	unlock_mutex = 1;
17668516Sdeischen	int	seqno;
17713546Sjulian
178115173Sdeischen	if (cond == NULL)
17968516Sdeischen		return (EINVAL);
18035027Sjb
18135027Sjb	/*
18235027Sjb	 * If the condition variable is statically initialized,
18335027Sjb	 * perform the dynamic initialization:
18435027Sjb	 */
18568516Sdeischen	if (*cond == NULL &&
186115173Sdeischen	    (rval = pthread_cond_init(cond, NULL)) != 0)
18768516Sdeischen		return (rval);
18868516Sdeischen
189115278Sdeischen	if (!_kse_isthreaded())
190115278Sdeischen		_kse_setthreaded(1);
191115278Sdeischen
19268516Sdeischen	/*
19368516Sdeischen	 * Enter a loop waiting for a condition signal or broadcast
19468516Sdeischen	 * to wake up this thread.  A loop is needed in case the waiting
19568516Sdeischen	 * thread is interrupted by a signal to execute a signal handler.
19668516Sdeischen	 * It is not (currently) possible to remain in the waiting queue
19768516Sdeischen	 * while running a handler.  Instead, the thread is interrupted
19868516Sdeischen	 * and backed out of the waiting queue prior to executing the
19968516Sdeischen	 * signal handler.
20068516Sdeischen	 */
20168516Sdeischen	do {
20248046Sjb		/* Lock the condition variable structure: */
203113658Sdeischen		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
20448046Sjb
20547424Sjb		/*
20647424Sjb		 * If the condvar was statically allocated, properly
20747424Sjb		 * initialize the tail queue.
20847424Sjb		 */
20947424Sjb		if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
21047424Sjb			TAILQ_INIT(&(*cond)->c_queue);
21147424Sjb			(*cond)->c_flags |= COND_FLAGS_INITED;
21247424Sjb		}
21347424Sjb
21417706Sjulian		/* Process according to condition variable type: */
21517706Sjulian		switch ((*cond)->c_type) {
21624827Sjb		/* Fast condition variable: */
21717706Sjulian		case COND_TYPE_FAST:
21844963Sjb			if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
21944963Sjb			    ((*cond)->c_mutex != *mutex))) {
22044963Sjb				/* Unlock the condition variable structure: */
221113658Sdeischen				THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
22240974Sdt
22344963Sjb				/* Return invalid argument error: */
22444963Sjb				rval = EINVAL;
22544963Sjb			} else {
22653812Salfred				/* Reset the timeout and interrupted flags: */
22771581Sdeischen				curthread->timeout = 0;
22871581Sdeischen				curthread->interrupted = 0;
22913546Sjulian
23040974Sdt				/*
23144963Sjb				 * Queue the running thread for the condition
23244963Sjb				 * variable:
23340974Sdt				 */
23471581Sdeischen				cond_queue_enq(*cond, curthread);
23513546Sjulian
23668516Sdeischen				/* Remember the mutex and sequence number: */
23744963Sjb				(*cond)->c_mutex = *mutex;
23868516Sdeischen				seqno = (*cond)->c_seqno;
23935509Sjb
24044963Sjb				/* Wait forever: */
24171581Sdeischen				curthread->wakeup_time.tv_sec = -1;
24244963Sjb
24344963Sjb				/* Unlock the mutex: */
244113658Sdeischen				if ((unlock_mutex != 0) &&
245113658Sdeischen				    ((rval = _mutex_cv_unlock(mutex)) != 0)) {
24644963Sjb					/*
24744963Sjb					 * Cannot unlock the mutex, so remove
24844963Sjb					 * the running thread from the condition
24944963Sjb					 * variable queue:
25044963Sjb					 */
25171581Sdeischen					cond_queue_remove(*cond, curthread);
25244963Sjb
25344963Sjb					/* Check for no more waiters: */
254113658Sdeischen					if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
25544963Sjb						(*cond)->c_mutex = NULL;
25644963Sjb
25744963Sjb					/* Unlock the condition variable structure: */
258113658Sdeischen					THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
259113658Sdeischen				}
260113658Sdeischen				else {
26144963Sjb					/*
262113658Sdeischen					 * Don't unlock the mutex the next
263113658Sdeischen					 * time through the loop (if the
264113658Sdeischen					 * thread has to be requeued after
265113658Sdeischen					 * handling a signal).
26644963Sjb					 */
267113658Sdeischen					unlock_mutex = 0;
26844963Sjb
269113658Sdeischen					/*
270113658Sdeischen					 * This thread is active and is in a
271113658Sdeischen					 * critical region (holding the cv
272113658Sdeischen					 * lock); we should be able to safely
273113658Sdeischen					 * set the state.
274113658Sdeischen					 */
275115080Sdeischen					THR_SCHED_LOCK(curthread, curthread);
276113658Sdeischen					THR_SET_STATE(curthread, PS_COND_WAIT);
27756277Sjasone
278113658Sdeischen					/* Remember the CV: */
279113658Sdeischen					curthread->data.cond = *cond;
280115080Sdeischen					THR_SCHED_UNLOCK(curthread, curthread);
28181918Sjasone
282113658Sdeischen					/* Unlock the CV structure: */
283113658Sdeischen					THR_LOCK_RELEASE(curthread,
284113658Sdeischen					    &(*cond)->c_lock);
285113658Sdeischen
286113658Sdeischen					/* Schedule the next thread: */
287113658Sdeischen					_thr_sched_switch(curthread);
288113658Sdeischen
289113658Sdeischen					curthread->data.cond = NULL;
290113658Sdeischen
29181918Sjasone					/*
292113658Sdeischen					 * XXX - This really isn't a good check
293113658Sdeischen					 * since there can be more than one
294113658Sdeischen					 * thread waiting on the CV.  Signals
295113658Sdeischen					 * sent to threads waiting on mutexes
296113658Sdeischen					 * or CVs should really be deferred
297113658Sdeischen					 * until the threads are no longer
298113658Sdeischen					 * waiting, but POSIX says that signals
299113658Sdeischen					 * should be sent "as soon as possible".
30081918Sjasone					 */
301113658Sdeischen					done = (seqno != (*cond)->c_seqno);
302113658Sdeischen
303113658Sdeischen					if (THR_IN_SYNCQ(curthread)) {
30456277Sjasone						/*
30553812Salfred						 * Lock the condition variable
30653812Salfred						 * while removing the thread.
30753812Salfred						 */
308113658Sdeischen						THR_LOCK_ACQUIRE(curthread,
309113658Sdeischen						    &(*cond)->c_lock);
31053812Salfred
31153812Salfred						cond_queue_remove(*cond,
31271581Sdeischen						    curthread);
31353812Salfred
31453812Salfred						/* Check for no more waiters: */
31553812Salfred						if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
31653812Salfred							(*cond)->c_mutex = NULL;
31753812Salfred
318113658Sdeischen						THR_LOCK_RELEASE(curthread,
319113658Sdeischen						    &(*cond)->c_lock);
320113658Sdeischen					}
32181918Sjasone
322113658Sdeischen					/*
323113658Sdeischen					 * Save the interrupted flag; locking
324113658Sdeischen					 * the mutex may destroy it.
325113658Sdeischen					 */
326113658Sdeischen					interrupted = curthread->interrupted;
327113658Sdeischen
328113658Sdeischen					/*
329113658Sdeischen					 * Note that even though this thread may
330113658Sdeischen					 * have been canceled, POSIX requires
331113658Sdeischen					 * that the mutex be reaquired prior to
332113658Sdeischen					 * cancellation.
333113658Sdeischen					 */
334114524Sdavidxu					if (done || interrupted) {
33581918Sjasone						rval = _mutex_cv_lock(mutex);
336114187Sdeischen						unlock_mutex = 1;
337114187Sdeischen					}
33844963Sjb				}
33940974Sdt			}
34017706Sjulian			break;
34113546Sjulian
34224827Sjb		/* Trap invalid condition variable types: */
34317706Sjulian		default:
34440974Sdt			/* Unlock the condition variable structure: */
345113658Sdeischen			THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
34640974Sdt
34717706Sjulian			/* Return an invalid argument error: */
34831402Salex			rval = EINVAL;
34917706Sjulian			break;
35017706Sjulian		}
35153812Salfred
35271581Sdeischen		if ((interrupted != 0) && (curthread->continuation != NULL))
35371581Sdeischen			curthread->continuation((void *) curthread);
35468516Sdeischen	} while ((done == 0) && (rval == 0));
35513546Sjulian
35613546Sjulian	/* Return the completion status: */
35713546Sjulian	return (rval);
35813546Sjulian}
35913546Sjulian
360115399Skan__strong_reference(_pthread_cond_wait, _thr_cond_wait);
361115399Skan
36213546Sjulianint
363113658Sdeischen__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
364113658Sdeischen{
365113658Sdeischen	struct pthread *curthread = _get_curthread();
366113658Sdeischen	int ret;
367113658Sdeischen
368113658Sdeischen	_thr_enter_cancellation_point(curthread);
369113658Sdeischen	ret = _pthread_cond_wait(cond, mutex);
370113658Sdeischen	_thr_leave_cancellation_point(curthread);
371113658Sdeischen	return (ret);
372113658Sdeischen}
373113658Sdeischen
374113658Sdeischenint
37571581Sdeischen_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
37613546Sjulian		       const struct timespec * abstime)
37713546Sjulian{
37871581Sdeischen	struct pthread	*curthread = _get_curthread();
37956277Sjasone	int	rval = 0;
38068516Sdeischen	int	done = 0;
38156277Sjasone	int	interrupted = 0;
382113658Sdeischen	int	unlock_mutex = 1;
38368516Sdeischen	int	seqno;
38413546Sjulian
385113870Sdeischen	THR_ASSERT(curthread->locklevel == 0,
386113870Sdeischen	    "cv_timedwait: locklevel is not zero!");
387113658Sdeischen
38863355Sjasone	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
389115173Sdeischen	    abstime->tv_nsec >= 1000000000)
39068516Sdeischen		return (EINVAL);
39135027Sjb	/*
39263355Sjasone	 * If the condition variable is statically initialized, perform dynamic
39363355Sjasone	 * initialization.
39435027Sjb	 */
395115173Sdeischen	if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0)
39668516Sdeischen		return (rval);
39768516Sdeischen
398115278Sdeischen	if (!_kse_isthreaded())
399115278Sdeischen		_kse_setthreaded(1);
400115278Sdeischen
40168516Sdeischen	/*
40268516Sdeischen	 * Enter a loop waiting for a condition signal or broadcast
40368516Sdeischen	 * to wake up this thread.  A loop is needed in case the waiting
40468516Sdeischen	 * thread is interrupted by a signal to execute a signal handler.
40568516Sdeischen	 * It is not (currently) possible to remain in the waiting queue
40668516Sdeischen	 * while running a handler.  Instead, the thread is interrupted
40768516Sdeischen	 * and backed out of the waiting queue prior to executing the
40868516Sdeischen	 * signal handler.
40968516Sdeischen	 */
41068516Sdeischen	do {
41148046Sjb		/* Lock the condition variable structure: */
412113658Sdeischen		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
41348046Sjb
41447424Sjb		/*
41547424Sjb		 * If the condvar was statically allocated, properly
41647424Sjb		 * initialize the tail queue.
41747424Sjb		 */
41847424Sjb		if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
41947424Sjb			TAILQ_INIT(&(*cond)->c_queue);
42047424Sjb			(*cond)->c_flags |= COND_FLAGS_INITED;
42147424Sjb		}
42247424Sjb
42317706Sjulian		/* Process according to condition variable type: */
42417706Sjulian		switch ((*cond)->c_type) {
42524827Sjb		/* Fast condition variable: */
42617706Sjulian		case COND_TYPE_FAST:
42744963Sjb			if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
42844963Sjb			    ((*cond)->c_mutex != *mutex))) {
42944963Sjb				/* Return invalid argument error: */
43044963Sjb				rval = EINVAL;
43113546Sjulian
43244963Sjb				/* Unlock the condition variable structure: */
433113658Sdeischen				THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
43444963Sjb			} else {
43544963Sjb				/* Set the wakeup time: */
436113658Sdeischen				curthread->wakeup_time.tv_sec = abstime->tv_sec;
43771581Sdeischen				curthread->wakeup_time.tv_nsec =
43844963Sjb				    abstime->tv_nsec;
43913546Sjulian
44053812Salfred				/* Reset the timeout and interrupted flags: */
44171581Sdeischen				curthread->timeout = 0;
44271581Sdeischen				curthread->interrupted = 0;
44313546Sjulian
44417706Sjulian				/*
44544963Sjb				 * Queue the running thread for the condition
44644963Sjb				 * variable:
44717706Sjulian				 */
44871581Sdeischen				cond_queue_enq(*cond, curthread);
44940974Sdt
45068516Sdeischen				/* Remember the mutex and sequence number: */
45144963Sjb				(*cond)->c_mutex = *mutex;
45268516Sdeischen				seqno = (*cond)->c_seqno;
45313546Sjulian
45444963Sjb				/* Unlock the mutex: */
455113658Sdeischen				if ((unlock_mutex != 0) &&
456113658Sdeischen				   ((rval = _mutex_cv_unlock(mutex)) != 0)) {
45744963Sjb					/*
458113658Sdeischen					 * Cannot unlock the mutex; remove the
459113658Sdeischen					 * running thread from the condition
46044963Sjb					 * variable queue:
46144963Sjb					 */
46271581Sdeischen					cond_queue_remove(*cond, curthread);
46344963Sjb
46444963Sjb					/* Check for no more waiters: */
46544963Sjb					if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
46644963Sjb						(*cond)->c_mutex = NULL;
46744963Sjb
46844963Sjb					/* Unlock the condition variable structure: */
469113658Sdeischen					THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
47044963Sjb				} else {
47144963Sjb					/*
472113658Sdeischen					 * Don't unlock the mutex the next
473113658Sdeischen					 * time through the loop (if the
474113658Sdeischen					 * thread has to be requeued after
475113658Sdeischen					 * handling a signal).
47644963Sjb					 */
477113658Sdeischen					unlock_mutex = 0;
47844963Sjb
479113658Sdeischen					/*
480113658Sdeischen					 * This thread is active and is in a
481113658Sdeischen					 * critical region (holding the cv
482113658Sdeischen					 * lock); we should be able to safely
483113658Sdeischen					 * set the state.
484113658Sdeischen					 */
485115080Sdeischen					THR_SCHED_LOCK(curthread, curthread);
486113658Sdeischen					THR_SET_STATE(curthread, PS_COND_WAIT);
48768516Sdeischen
488113658Sdeischen					/* Remember the CV: */
489113658Sdeischen					curthread->data.cond = *cond;
490115080Sdeischen					THR_SCHED_UNLOCK(curthread, curthread);
49181918Sjasone
492113658Sdeischen					/* Unlock the CV structure: */
493113658Sdeischen					THR_LOCK_RELEASE(curthread,
494113658Sdeischen					    &(*cond)->c_lock);
495113658Sdeischen
496113658Sdeischen					/* Schedule the next thread: */
497113658Sdeischen					_thr_sched_switch(curthread);
498113658Sdeischen
499113658Sdeischen					curthread->data.cond = NULL;
500113658Sdeischen
50153812Salfred					/*
502113658Sdeischen					 * XXX - This really isn't a good check
503113658Sdeischen					 * since there can be more than one
504113658Sdeischen					 * thread waiting on the CV.  Signals
505113658Sdeischen					 * sent to threads waiting on mutexes
506113658Sdeischen					 * or CVs should really be deferred
507113658Sdeischen					 * until the threads are no longer
508113658Sdeischen					 * waiting, but POSIX says that signals
509113658Sdeischen					 * should be sent "as soon as possible".
51053812Salfred					 */
511113658Sdeischen					done = (seqno != (*cond)->c_seqno);
512113658Sdeischen
513113658Sdeischen					if (THR_IN_CONDQ(curthread)) {
51481918Sjasone						/*
51581918Sjasone						 * Lock the condition variable
51681918Sjasone						 * while removing the thread.
51781918Sjasone						 */
518113658Sdeischen						THR_LOCK_ACQUIRE(curthread,
519113658Sdeischen						    &(*cond)->c_lock);
52044963Sjb
52144963Sjb						cond_queue_remove(*cond,
52271581Sdeischen						    curthread);
52344963Sjb
52444963Sjb						/* Check for no more waiters: */
52544963Sjb						if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
52644963Sjb							(*cond)->c_mutex = NULL;
52744963Sjb
528113658Sdeischen						THR_LOCK_RELEASE(curthread,
529113658Sdeischen						    &(*cond)->c_lock);
530113658Sdeischen					}
53144963Sjb
532113658Sdeischen					/*
533113658Sdeischen					 * Save the interrupted flag; locking
534113658Sdeischen					 * the mutex may destroy it.
535113658Sdeischen					 */
536113658Sdeischen					interrupted = curthread->interrupted;
537113658Sdeischen					if (curthread->timeout != 0) {
538113658Sdeischen						/* The wait timedout. */
539113658Sdeischen						rval = ETIMEDOUT;
540113658Sdeischen						(void)_mutex_cv_lock(mutex);
541117165Sdavidxu					} else if (interrupted || done) {
54281918Sjasone						rval = _mutex_cv_lock(mutex);
543117165Sdavidxu						unlock_mutex = 1;
544117165Sdavidxu					}
54517706Sjulian				}
54613546Sjulian			}
54717706Sjulian			break;
54817706Sjulian
54924827Sjb		/* Trap invalid condition variable types: */
55017706Sjulian		default:
55140974Sdt			/* Unlock the condition variable structure: */
552113658Sdeischen			THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
55340974Sdt
55417706Sjulian			/* Return an invalid argument error: */
55531402Salex			rval = EINVAL;
55617706Sjulian			break;
55713546Sjulian		}
55813546Sjulian
55971581Sdeischen		if ((interrupted != 0) && (curthread->continuation != NULL))
560113658Sdeischen			curthread->continuation((void *)curthread);
56168516Sdeischen	} while ((done == 0) && (rval == 0));
56213546Sjulian
56313546Sjulian	/* Return the completion status: */
56413546Sjulian	return (rval);
56513546Sjulian}
56613546Sjulian
56713546Sjulianint
568113658Sdeischen__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
569113658Sdeischen		       const struct timespec *abstime)
570113658Sdeischen{
571113658Sdeischen	struct pthread *curthread = _get_curthread();
572113658Sdeischen	int ret;
573113658Sdeischen
574113658Sdeischen	_thr_enter_cancellation_point(curthread);
575113658Sdeischen	ret = _pthread_cond_timedwait(cond, mutex, abstime);
576113658Sdeischen	_thr_leave_cancellation_point(curthread);
577113658Sdeischen	return (ret);
578113658Sdeischen}
579113658Sdeischen
580113658Sdeischen
581113658Sdeischenint
58271581Sdeischen_pthread_cond_signal(pthread_cond_t * cond)
58313546Sjulian{
584113658Sdeischen	struct pthread	*curthread = _get_curthread();
585113658Sdeischen	struct pthread	*pthread;
586117907Sdeischen	struct kse_mailbox *kmbx;
587113658Sdeischen	int		rval = 0;
58813546Sjulian
589113870Sdeischen	THR_ASSERT(curthread->locklevel == 0,
590113870Sdeischen	    "cv_timedwait: locklevel is not zero!");
59163355Sjasone	if (cond == NULL)
59231402Salex		rval = EINVAL;
59363355Sjasone       /*
59463355Sjasone        * If the condition variable is statically initialized, perform dynamic
59563355Sjasone        * initialization.
59663355Sjasone        */
59768844Sdeischen	else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) {
59835509Sjb		/* Lock the condition variable structure: */
599113658Sdeischen		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
60013546Sjulian
60117706Sjulian		/* Process according to condition variable type: */
60217706Sjulian		switch ((*cond)->c_type) {
60324827Sjb		/* Fast condition variable: */
60417706Sjulian		case COND_TYPE_FAST:
60568516Sdeischen			/* Increment the sequence number: */
60668516Sdeischen			(*cond)->c_seqno++;
60768516Sdeischen
608113658Sdeischen			/*
609113658Sdeischen			 * Wakeups have to be done with the CV lock held;
610113658Sdeischen			 * otherwise there is a race condition where the
611113658Sdeischen			 * thread can timeout, run on another KSE, and enter
612113658Sdeischen			 * another blocking state (including blocking on a CV).
613113658Sdeischen			 */
614113658Sdeischen			if ((pthread = TAILQ_FIRST(&(*cond)->c_queue))
615113658Sdeischen			    != NULL) {
616113658Sdeischen				THR_SCHED_LOCK(curthread, pthread);
617113658Sdeischen				cond_queue_remove(*cond, pthread);
618117714Sdeischen				if ((pthread->kseg == curthread->kseg) &&
619117714Sdeischen				    (pthread->active_priority >
620117714Sdeischen				    curthread->active_priority))
621117714Sdeischen					curthread->critical_yield = 1;
622117907Sdeischen				kmbx = _thr_setrunnable_unlocked(pthread);
623113658Sdeischen				THR_SCHED_UNLOCK(curthread, pthread);
624117907Sdeischen				if (kmbx != NULL)
625117907Sdeischen					kse_wakeup(kmbx);
62661681Sjasone			}
62744963Sjb			/* Check for no more waiters: */
62844963Sjb			if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
62944963Sjb				(*cond)->c_mutex = NULL;
63017706Sjulian			break;
63117706Sjulian
63224827Sjb		/* Trap invalid condition variable types: */
63317706Sjulian		default:
63417706Sjulian			/* Return an invalid argument error: */
63531402Salex			rval = EINVAL;
63617706Sjulian			break;
63713546Sjulian		}
63813546Sjulian
63935509Sjb		/* Unlock the condition variable structure: */
640113658Sdeischen		THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
64113546Sjulian	}
64213546Sjulian
64313546Sjulian	/* Return the completion status: */
64413546Sjulian	return (rval);
64513546Sjulian}
64613546Sjulian
647115399Skan__strong_reference(_pthread_cond_signal, _thr_cond_signal);
648115399Skan
64913546Sjulianint
65071581Sdeischen_pthread_cond_broadcast(pthread_cond_t * cond)
65113546Sjulian{
652113658Sdeischen	struct pthread	*curthread = _get_curthread();
653113658Sdeischen	struct pthread	*pthread;
654117907Sdeischen	struct kse_mailbox *kmbx;
655113658Sdeischen	int		rval = 0;
65613546Sjulian
657113870Sdeischen	THR_ASSERT(curthread->locklevel == 0,
658113870Sdeischen	    "cv_timedwait: locklevel is not zero!");
65963355Sjasone	if (cond == NULL)
66035509Sjb		rval = EINVAL;
66163355Sjasone       /*
66263355Sjasone        * If the condition variable is statically initialized, perform dynamic
66363355Sjasone        * initialization.
66463355Sjasone        */
66568844Sdeischen	else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) {
66635509Sjb		/* Lock the condition variable structure: */
667113658Sdeischen		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
66813546Sjulian
66935509Sjb		/* Process according to condition variable type: */
67035509Sjb		switch ((*cond)->c_type) {
67135509Sjb		/* Fast condition variable: */
67235509Sjb		case COND_TYPE_FAST:
67368516Sdeischen			/* Increment the sequence number: */
67468516Sdeischen			(*cond)->c_seqno++;
67568516Sdeischen
67635509Sjb			/*
67735509Sjb			 * Enter a loop to bring all threads off the
67835509Sjb			 * condition queue:
67935509Sjb			 */
680113658Sdeischen			while ((pthread = TAILQ_FIRST(&(*cond)->c_queue))
681113658Sdeischen			    != NULL) {
682113658Sdeischen				THR_SCHED_LOCK(curthread, pthread);
683113658Sdeischen				cond_queue_remove(*cond, pthread);
684117714Sdeischen				if ((pthread->kseg == curthread->kseg) &&
685117714Sdeischen				    (pthread->active_priority >
686117714Sdeischen				    curthread->active_priority))
687117714Sdeischen					curthread->critical_yield = 1;
688117907Sdeischen				kmbx = _thr_setrunnable_unlocked(pthread);
689113658Sdeischen				THR_SCHED_UNLOCK(curthread, pthread);
690117907Sdeischen				if (kmbx != NULL)
691117907Sdeischen					kse_wakeup(kmbx);
69235509Sjb			}
69344963Sjb
69444963Sjb			/* There are no more waiting threads: */
69544963Sjb			(*cond)->c_mutex = NULL;
69635509Sjb			break;
697115399Skan
69835509Sjb		/* Trap invalid condition variable types: */
69935509Sjb		default:
70035509Sjb			/* Return an invalid argument error: */
70135509Sjb			rval = EINVAL;
70235509Sjb			break;
70313546Sjulian		}
70413546Sjulian
70535509Sjb		/* Unlock the condition variable structure: */
706113658Sdeischen		THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
70713546Sjulian	}
70813546Sjulian
70913546Sjulian	/* Return the completion status: */
71013546Sjulian	return (rval);
71113546Sjulian}
71244963Sjb
713115399Skan__strong_reference(_pthread_cond_broadcast, _thr_cond_broadcast);
714115399Skan
71567097Sdeischenvoid
716113658Sdeischen_cond_wait_backout(struct pthread *curthread)
71767097Sdeischen{
71867097Sdeischen	pthread_cond_t	cond;
71967097Sdeischen
720113658Sdeischen	cond = curthread->data.cond;
72167097Sdeischen	if (cond != NULL) {
72267097Sdeischen		/* Lock the condition variable structure: */
723113658Sdeischen		THR_LOCK_ACQUIRE(curthread, &cond->c_lock);
72467097Sdeischen
72567097Sdeischen		/* Process according to condition variable type: */
72667097Sdeischen		switch (cond->c_type) {
72767097Sdeischen		/* Fast condition variable: */
72867097Sdeischen		case COND_TYPE_FAST:
729113658Sdeischen			cond_queue_remove(cond, curthread);
73067097Sdeischen
73167097Sdeischen			/* Check for no more waiters: */
73267097Sdeischen			if (TAILQ_FIRST(&cond->c_queue) == NULL)
73367097Sdeischen				cond->c_mutex = NULL;
73467097Sdeischen			break;
73567097Sdeischen
73667097Sdeischen		default:
73767097Sdeischen			break;
73867097Sdeischen		}
73967097Sdeischen
74067097Sdeischen		/* Unlock the condition variable structure: */
741113658Sdeischen		THR_LOCK_RELEASE(curthread, &cond->c_lock);
74267097Sdeischen	}
74367097Sdeischen}
74467097Sdeischen
74544963Sjb/*
74644963Sjb * Dequeue a waiting thread from the head of a condition queue in
74744963Sjb * descending priority order.
74844963Sjb */
749113658Sdeischenstatic inline struct pthread *
75044963Sjbcond_queue_deq(pthread_cond_t cond)
75144963Sjb{
752113658Sdeischen	struct pthread	*pthread;
75344963Sjb
75453812Salfred	while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
75567097Sdeischen		TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
756117165Sdavidxu		THR_CONDQ_CLEAR(pthread);
75753812Salfred		if ((pthread->timeout == 0) && (pthread->interrupted == 0))
75853812Salfred			/*
75953812Salfred			 * Only exit the loop when we find a thread
76053812Salfred			 * that hasn't timed out or been canceled;
76153812Salfred			 * those threads are already running and don't
76253812Salfred			 * need their run state changed.
76353812Salfred			 */
76453812Salfred			break;
76544963Sjb	}
76644963Sjb
767113658Sdeischen	return (pthread);
76844963Sjb}
76944963Sjb
77044963Sjb/*
77144963Sjb * Remove a waiting thread from a condition queue in descending priority
77244963Sjb * order.
77344963Sjb */
77444963Sjbstatic inline void
775113658Sdeischencond_queue_remove(pthread_cond_t cond, struct pthread *pthread)
77644963Sjb{
77744963Sjb	/*
77844963Sjb	 * Because pthread_cond_timedwait() can timeout as well
77944963Sjb	 * as be signaled by another thread, it is necessary to
78044963Sjb	 * guard against removing the thread from the queue if
78144963Sjb	 * it isn't in the queue.
78244963Sjb	 */
783113658Sdeischen	if (THR_IN_CONDQ(pthread)) {
78467097Sdeischen		TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
785113658Sdeischen		THR_CONDQ_CLEAR(pthread);
78644963Sjb	}
78744963Sjb}
78844963Sjb
78944963Sjb/*
79044963Sjb * Enqueue a waiting thread to a condition queue in descending priority
79144963Sjb * order.
79244963Sjb */
79344963Sjbstatic inline void
794113658Sdeischencond_queue_enq(pthread_cond_t cond, struct pthread *pthread)
79544963Sjb{
796113658Sdeischen	struct pthread *tid = TAILQ_LAST(&cond->c_queue, cond_head);
79744963Sjb
798113658Sdeischen	THR_ASSERT(!THR_IN_SYNCQ(pthread),
799113658Sdeischen	    "cond_queue_enq: thread already queued!");
80067097Sdeischen
80144963Sjb	/*
80244963Sjb	 * For the common case of all threads having equal priority,
80344963Sjb	 * we perform a quick check against the priority of the thread
80444963Sjb	 * at the tail of the queue.
80544963Sjb	 */
80644963Sjb	if ((tid == NULL) || (pthread->active_priority <= tid->active_priority))
80767097Sdeischen		TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe);
80844963Sjb	else {
80944963Sjb		tid = TAILQ_FIRST(&cond->c_queue);
81044963Sjb		while (pthread->active_priority <= tid->active_priority)
81167097Sdeischen			tid = TAILQ_NEXT(tid, sqe);
81267097Sdeischen		TAILQ_INSERT_BEFORE(tid, pthread, sqe);
81344963Sjb	}
814113658Sdeischen	THR_CONDQ_SET(pthread);
81567097Sdeischen	pthread->data.cond = cond;
81644963Sjb}
817