thr_cond.c revision 114187
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 114187 2003-04-28 23:56:12Z deischen $
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_IN_CONDQ(thr)	(((thr)->sflags & THR_FLAGS_IN_SYNCQ) != 0)
42113658Sdeischen#define	THR_CONDQ_SET(thr)	(thr)->sflags |= THR_FLAGS_IN_SYNCQ
43113658Sdeischen#define	THR_CONDQ_CLEAR(thr)	(thr)->sflags &= ~THR_FLAGS_IN_SYNCQ
44113658Sdeischen
4544963Sjb/*
4644963Sjb * Prototypes
4744963Sjb */
48113658Sdeischenstatic inline struct pthread	*cond_queue_deq(pthread_cond_t);
49113658Sdeischenstatic inline void		cond_queue_remove(pthread_cond_t, pthread_t);
50113658Sdeischenstatic inline void		cond_queue_enq(pthread_cond_t, pthread_t);
5144963Sjb
5275369Sdeischen__weak_reference(_pthread_cond_init, pthread_cond_init);
5375369Sdeischen__weak_reference(_pthread_cond_destroy, pthread_cond_destroy);
5475369Sdeischen__weak_reference(_pthread_cond_wait, pthread_cond_wait);
5575369Sdeischen__weak_reference(_pthread_cond_timedwait, pthread_cond_timedwait);
5675369Sdeischen__weak_reference(_pthread_cond_signal, pthread_cond_signal);
5775369Sdeischen__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
5871581Sdeischen
5971581Sdeischen
6048046Sjbint
6171581Sdeischen_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
6213546Sjulian{
6313546Sjulian	enum pthread_cond_type type;
6417706Sjulian	pthread_cond_t	pcond;
65113658Sdeischen	int		flags;
6613546Sjulian	int             rval = 0;
6713546Sjulian
6835509Sjb	if (cond == NULL)
6931402Salex		rval = EINVAL;
7035509Sjb	else {
7117706Sjulian		/*
7224827Sjb		 * Check if a pointer to a condition variable attribute
7324827Sjb		 * structure was passed by the caller:
7417706Sjulian		 */
7517706Sjulian		if (cond_attr != NULL && *cond_attr != NULL) {
7617706Sjulian			/* Default to a fast condition variable: */
7717706Sjulian			type = (*cond_attr)->c_type;
78113658Sdeischen			flags = (*cond_attr)->c_flags;
7917706Sjulian		} else {
8017706Sjulian			/* Default to a fast condition variable: */
8117706Sjulian			type = COND_TYPE_FAST;
82113658Sdeischen			flags = 0;
8317706Sjulian		}
8413546Sjulian
8517706Sjulian		/* Process according to condition variable type: */
8617706Sjulian		switch (type) {
8724827Sjb		/* Fast condition variable: */
8817706Sjulian		case COND_TYPE_FAST:
8917706Sjulian			/* Nothing to do here. */
9017706Sjulian			break;
9113546Sjulian
9224827Sjb		/* Trap invalid condition variable types: */
9317706Sjulian		default:
9417706Sjulian			/* Return an invalid argument error: */
9531402Salex			rval = EINVAL;
9617706Sjulian			break;
9717706Sjulian		}
9813546Sjulian
9917706Sjulian		/* Check for no errors: */
10017706Sjulian		if (rval == 0) {
10124827Sjb			if ((pcond = (pthread_cond_t)
10224827Sjb			    malloc(sizeof(struct pthread_cond))) == NULL) {
10331402Salex				rval = ENOMEM;
104113658Sdeischen			} else if (_lock_init(&pcond->c_lock, LCK_ADAPTIVE,
105113786Sdeischen			    _thr_lock_wait, _thr_lock_wakeup) != 0) {
106113658Sdeischen				free(pcond);
107113658Sdeischen				rval = ENOMEM;
10817706Sjulian			} else {
10917706Sjulian				/*
11017706Sjulian				 * Initialise the condition variable
11117706Sjulian				 * structure:
11217706Sjulian				 */
11344963Sjb				TAILQ_INIT(&pcond->c_queue);
11417706Sjulian				pcond->c_flags |= COND_FLAGS_INITED;
11517706Sjulian				pcond->c_type = type;
11644963Sjb				pcond->c_mutex = NULL;
11768516Sdeischen				pcond->c_seqno = 0;
11817706Sjulian				*cond = pcond;
11917706Sjulian			}
12017706Sjulian		}
12113546Sjulian	}
12213546Sjulian	/* Return the completion status: */
12313546Sjulian	return (rval);
12413546Sjulian}
12513546Sjulian
12613546Sjulianint
12771581Sdeischen_pthread_cond_destroy(pthread_cond_t *cond)
12813546Sjulian{
129113658Sdeischen	struct pthread_cond	*cv;
130113658Sdeischen	struct pthread		*curthread = _get_curthread();
131113658Sdeischen	int			rval = 0;
13213546Sjulian
13335509Sjb	if (cond == NULL || *cond == NULL)
13431402Salex		rval = EINVAL;
13535509Sjb	else {
13635509Sjb		/* Lock the condition variable structure: */
137113658Sdeischen		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
13813546Sjulian
13935509Sjb		/*
140113658Sdeischen		 * NULL the caller's pointer now that the condition
141113658Sdeischen		 * variable has been destroyed:
142113658Sdeischen		 */
143113658Sdeischen		cv = *cond;
144113658Sdeischen		*cond = NULL;
145113658Sdeischen
146113658Sdeischen		/* Unlock the condition variable structure: */
147113658Sdeischen		THR_LOCK_RELEASE(curthread, &cv->c_lock);
148113658Sdeischen
149113658Sdeischen		/*
15035509Sjb		 * Free the memory allocated for the condition
15135509Sjb		 * variable structure:
15235509Sjb		 */
153113658Sdeischen		free(cv);
15417706Sjulian
15513546Sjulian	}
15613546Sjulian	/* Return the completion status: */
15713546Sjulian	return (rval);
15813546Sjulian}
15913546Sjulian
16013546Sjulianint
16171581Sdeischen_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
16213546Sjulian{
16371581Sdeischen	struct pthread	*curthread = _get_curthread();
16456277Sjasone	int	rval = 0;
16568516Sdeischen	int	done = 0;
16656277Sjasone	int	interrupted = 0;
167113658Sdeischen	int	unlock_mutex = 1;
16868516Sdeischen	int	seqno;
16913546Sjulian
170113658Sdeischen	_thr_enter_cancellation_point(curthread);
171113658Sdeischen
172113658Sdeischen	if (cond == NULL) {
173113658Sdeischen		_thr_leave_cancellation_point(curthread);
17468516Sdeischen		return (EINVAL);
175113658Sdeischen	}
17635027Sjb
17735027Sjb	/*
17835027Sjb	 * If the condition variable is statically initialized,
17935027Sjb	 * perform the dynamic initialization:
18035027Sjb	 */
18168516Sdeischen	if (*cond == NULL &&
182113658Sdeischen	    (rval = pthread_cond_init(cond, NULL)) != 0) {
183113658Sdeischen		_thr_leave_cancellation_point(curthread);
18468516Sdeischen		return (rval);
185113658Sdeischen	}
18668516Sdeischen
18768516Sdeischen	/*
18868516Sdeischen	 * Enter a loop waiting for a condition signal or broadcast
18968516Sdeischen	 * to wake up this thread.  A loop is needed in case the waiting
19068516Sdeischen	 * thread is interrupted by a signal to execute a signal handler.
19168516Sdeischen	 * It is not (currently) possible to remain in the waiting queue
19268516Sdeischen	 * while running a handler.  Instead, the thread is interrupted
19368516Sdeischen	 * and backed out of the waiting queue prior to executing the
19468516Sdeischen	 * signal handler.
19568516Sdeischen	 */
19668516Sdeischen	do {
19748046Sjb		/* Lock the condition variable structure: */
198113658Sdeischen		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
19948046Sjb
20047424Sjb		/*
20147424Sjb		 * If the condvar was statically allocated, properly
20247424Sjb		 * initialize the tail queue.
20347424Sjb		 */
20447424Sjb		if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
20547424Sjb			TAILQ_INIT(&(*cond)->c_queue);
20647424Sjb			(*cond)->c_flags |= COND_FLAGS_INITED;
20747424Sjb		}
20847424Sjb
20917706Sjulian		/* Process according to condition variable type: */
21017706Sjulian		switch ((*cond)->c_type) {
21124827Sjb		/* Fast condition variable: */
21217706Sjulian		case COND_TYPE_FAST:
21344963Sjb			if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
21444963Sjb			    ((*cond)->c_mutex != *mutex))) {
21544963Sjb				/* Unlock the condition variable structure: */
216113658Sdeischen				THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
21740974Sdt
21844963Sjb				/* Return invalid argument error: */
21944963Sjb				rval = EINVAL;
22044963Sjb			} else {
22153812Salfred				/* Reset the timeout and interrupted flags: */
22271581Sdeischen				curthread->timeout = 0;
22371581Sdeischen				curthread->interrupted = 0;
22413546Sjulian
22540974Sdt				/*
22644963Sjb				 * Queue the running thread for the condition
22744963Sjb				 * variable:
22840974Sdt				 */
22971581Sdeischen				cond_queue_enq(*cond, curthread);
23013546Sjulian
23168516Sdeischen				/* Remember the mutex and sequence number: */
23244963Sjb				(*cond)->c_mutex = *mutex;
23368516Sdeischen				seqno = (*cond)->c_seqno;
23435509Sjb
23544963Sjb				/* Wait forever: */
23671581Sdeischen				curthread->wakeup_time.tv_sec = -1;
23744963Sjb
23844963Sjb				/* Unlock the mutex: */
239113658Sdeischen				if ((unlock_mutex != 0) &&
240113658Sdeischen				    ((rval = _mutex_cv_unlock(mutex)) != 0)) {
24144963Sjb					/*
24244963Sjb					 * Cannot unlock the mutex, so remove
24344963Sjb					 * the running thread from the condition
24444963Sjb					 * variable queue:
24544963Sjb					 */
24671581Sdeischen					cond_queue_remove(*cond, curthread);
24744963Sjb
24844963Sjb					/* Check for no more waiters: */
249113658Sdeischen					if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
25044963Sjb						(*cond)->c_mutex = NULL;
25144963Sjb
25244963Sjb					/* Unlock the condition variable structure: */
253113658Sdeischen					THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
254113658Sdeischen				}
255113658Sdeischen				else {
25644963Sjb					/*
257113658Sdeischen					 * Don't unlock the mutex the next
258113658Sdeischen					 * time through the loop (if the
259113658Sdeischen					 * thread has to be requeued after
260113658Sdeischen					 * handling a signal).
26144963Sjb					 */
262113658Sdeischen					unlock_mutex = 0;
26344963Sjb
264113658Sdeischen					/*
265113658Sdeischen					 * This thread is active and is in a
266113658Sdeischen					 * critical region (holding the cv
267113658Sdeischen					 * lock); we should be able to safely
268113658Sdeischen					 * set the state.
269113658Sdeischen					 */
270114187Sdeischen					THR_LOCK_SWITCH(curthread);
271113658Sdeischen					THR_SET_STATE(curthread, PS_COND_WAIT);
27256277Sjasone
273113658Sdeischen					/* Remember the CV: */
274113658Sdeischen					curthread->data.cond = *cond;
27581918Sjasone
276113658Sdeischen					/* Unlock the CV structure: */
277113658Sdeischen					THR_LOCK_RELEASE(curthread,
278113658Sdeischen					    &(*cond)->c_lock);
279113658Sdeischen
280113658Sdeischen					/* Schedule the next thread: */
281113658Sdeischen					_thr_sched_switch(curthread);
282113658Sdeischen
283113658Sdeischen					curthread->data.cond = NULL;
284114187Sdeischen					THR_UNLOCK_SWITCH(curthread);
285113658Sdeischen
28681918Sjasone					/*
287113658Sdeischen					 * XXX - This really isn't a good check
288113658Sdeischen					 * since there can be more than one
289113658Sdeischen					 * thread waiting on the CV.  Signals
290113658Sdeischen					 * sent to threads waiting on mutexes
291113658Sdeischen					 * or CVs should really be deferred
292113658Sdeischen					 * until the threads are no longer
293113658Sdeischen					 * waiting, but POSIX says that signals
294113658Sdeischen					 * should be sent "as soon as possible".
29581918Sjasone					 */
296113658Sdeischen					done = (seqno != (*cond)->c_seqno);
297113658Sdeischen
298113658Sdeischen					if (THR_IN_SYNCQ(curthread)) {
29956277Sjasone						/*
30053812Salfred						 * Lock the condition variable
30153812Salfred						 * while removing the thread.
30253812Salfred						 */
303113658Sdeischen						THR_LOCK_ACQUIRE(curthread,
304113658Sdeischen						    &(*cond)->c_lock);
30553812Salfred
30653812Salfred						cond_queue_remove(*cond,
30771581Sdeischen						    curthread);
30853812Salfred
30953812Salfred						/* Check for no more waiters: */
31053812Salfred						if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
31153812Salfred							(*cond)->c_mutex = NULL;
31253812Salfred
313113658Sdeischen						THR_LOCK_RELEASE(curthread,
314113658Sdeischen						    &(*cond)->c_lock);
315113658Sdeischen					}
31681918Sjasone
317113658Sdeischen					/*
318113658Sdeischen					 * Save the interrupted flag; locking
319113658Sdeischen					 * the mutex may destroy it.
320113658Sdeischen					 */
321113658Sdeischen					interrupted = curthread->interrupted;
322113658Sdeischen
323113658Sdeischen					/*
324113658Sdeischen					 * Note that even though this thread may
325113658Sdeischen					 * have been canceled, POSIX requires
326113658Sdeischen					 * that the mutex be reaquired prior to
327113658Sdeischen					 * cancellation.
328113658Sdeischen					 */
329114187Sdeischen					if (done != 0) {
33081918Sjasone						rval = _mutex_cv_lock(mutex);
331114187Sdeischen						unlock_mutex = 1;
332114187Sdeischen					}
33344963Sjb				}
33440974Sdt			}
33517706Sjulian			break;
33613546Sjulian
33724827Sjb		/* Trap invalid condition variable types: */
33817706Sjulian		default:
33940974Sdt			/* Unlock the condition variable structure: */
340113658Sdeischen			THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
34140974Sdt
34217706Sjulian			/* Return an invalid argument error: */
34331402Salex			rval = EINVAL;
34417706Sjulian			break;
34517706Sjulian		}
34653812Salfred
34771581Sdeischen		if ((interrupted != 0) && (curthread->continuation != NULL))
34871581Sdeischen			curthread->continuation((void *) curthread);
34968516Sdeischen	} while ((done == 0) && (rval == 0));
35013546Sjulian
351113658Sdeischen	_thr_leave_cancellation_point(curthread);
35256698Sjasone
35313546Sjulian	/* Return the completion status: */
35413546Sjulian	return (rval);
35513546Sjulian}
35613546Sjulian
35713546Sjulianint
358113658Sdeischen__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
359113658Sdeischen{
360113658Sdeischen	struct pthread *curthread = _get_curthread();
361113658Sdeischen	int ret;
362113658Sdeischen
363113658Sdeischen	_thr_enter_cancellation_point(curthread);
364113658Sdeischen	ret = _pthread_cond_wait(cond, mutex);
365113658Sdeischen	_thr_leave_cancellation_point(curthread);
366113658Sdeischen	return (ret);
367113658Sdeischen}
368113658Sdeischen
369113658Sdeischenint
37071581Sdeischen_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
37113546Sjulian		       const struct timespec * abstime)
37213546Sjulian{
37371581Sdeischen	struct pthread	*curthread = _get_curthread();
37456277Sjasone	int	rval = 0;
37568516Sdeischen	int	done = 0;
37656277Sjasone	int	interrupted = 0;
377113658Sdeischen	int	unlock_mutex = 1;
37868516Sdeischen	int	seqno;
37913546Sjulian
380113870Sdeischen	THR_ASSERT(curthread->locklevel == 0,
381113870Sdeischen	    "cv_timedwait: locklevel is not zero!");
382113658Sdeischen	_thr_enter_cancellation_point(curthread);
383113658Sdeischen
38463355Sjasone	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
385113658Sdeischen	    abstime->tv_nsec >= 1000000000) {
386113658Sdeischen		_thr_leave_cancellation_point(curthread);
38768516Sdeischen		return (EINVAL);
388113658Sdeischen	}
38935027Sjb	/*
39063355Sjasone	 * If the condition variable is statically initialized, perform dynamic
39163355Sjasone	 * initialization.
39235027Sjb	 */
393113658Sdeischen	if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0) {
394113658Sdeischen		_thr_leave_cancellation_point(curthread);
39568516Sdeischen		return (rval);
396113658Sdeischen	}
39768516Sdeischen
39868516Sdeischen	/*
39968516Sdeischen	 * Enter a loop waiting for a condition signal or broadcast
40068516Sdeischen	 * to wake up this thread.  A loop is needed in case the waiting
40168516Sdeischen	 * thread is interrupted by a signal to execute a signal handler.
40268516Sdeischen	 * It is not (currently) possible to remain in the waiting queue
40368516Sdeischen	 * while running a handler.  Instead, the thread is interrupted
40468516Sdeischen	 * and backed out of the waiting queue prior to executing the
40568516Sdeischen	 * signal handler.
40668516Sdeischen	 */
40768516Sdeischen	do {
40848046Sjb		/* Lock the condition variable structure: */
409113658Sdeischen		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
41048046Sjb
41147424Sjb		/*
41247424Sjb		 * If the condvar was statically allocated, properly
41347424Sjb		 * initialize the tail queue.
41447424Sjb		 */
41547424Sjb		if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
41647424Sjb			TAILQ_INIT(&(*cond)->c_queue);
41747424Sjb			(*cond)->c_flags |= COND_FLAGS_INITED;
41847424Sjb		}
41947424Sjb
42017706Sjulian		/* Process according to condition variable type: */
42117706Sjulian		switch ((*cond)->c_type) {
42224827Sjb		/* Fast condition variable: */
42317706Sjulian		case COND_TYPE_FAST:
42444963Sjb			if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
42544963Sjb			    ((*cond)->c_mutex != *mutex))) {
42644963Sjb				/* Return invalid argument error: */
42744963Sjb				rval = EINVAL;
42813546Sjulian
42944963Sjb				/* Unlock the condition variable structure: */
430113658Sdeischen				THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
43144963Sjb			} else {
43244963Sjb				/* Set the wakeup time: */
433113658Sdeischen				curthread->wakeup_time.tv_sec = abstime->tv_sec;
43471581Sdeischen				curthread->wakeup_time.tv_nsec =
43544963Sjb				    abstime->tv_nsec;
43613546Sjulian
43753812Salfred				/* Reset the timeout and interrupted flags: */
43871581Sdeischen				curthread->timeout = 0;
43971581Sdeischen				curthread->interrupted = 0;
44013546Sjulian
44117706Sjulian				/*
44244963Sjb				 * Queue the running thread for the condition
44344963Sjb				 * variable:
44417706Sjulian				 */
44571581Sdeischen				cond_queue_enq(*cond, curthread);
44640974Sdt
44768516Sdeischen				/* Remember the mutex and sequence number: */
44844963Sjb				(*cond)->c_mutex = *mutex;
44968516Sdeischen				seqno = (*cond)->c_seqno;
45013546Sjulian
45144963Sjb				/* Unlock the mutex: */
452113658Sdeischen				if ((unlock_mutex != 0) &&
453113658Sdeischen				   ((rval = _mutex_cv_unlock(mutex)) != 0)) {
45444963Sjb					/*
455113658Sdeischen					 * Cannot unlock the mutex; remove the
456113658Sdeischen					 * running thread from the condition
45744963Sjb					 * variable queue:
45844963Sjb					 */
45971581Sdeischen					cond_queue_remove(*cond, curthread);
46044963Sjb
46144963Sjb					/* Check for no more waiters: */
46244963Sjb					if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
46344963Sjb						(*cond)->c_mutex = NULL;
46444963Sjb
46544963Sjb					/* Unlock the condition variable structure: */
466113658Sdeischen					THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
46744963Sjb				} else {
46844963Sjb					/*
469113658Sdeischen					 * Don't unlock the mutex the next
470113658Sdeischen					 * time through the loop (if the
471113658Sdeischen					 * thread has to be requeued after
472113658Sdeischen					 * handling a signal).
47344963Sjb					 */
474113658Sdeischen					unlock_mutex = 0;
47544963Sjb
476113658Sdeischen					/*
477113658Sdeischen					 * This thread is active and is in a
478113658Sdeischen					 * critical region (holding the cv
479113658Sdeischen					 * lock); we should be able to safely
480113658Sdeischen					 * set the state.
481113658Sdeischen					 */
482114187Sdeischen					THR_LOCK_SWITCH(curthread);
483113658Sdeischen					THR_SET_STATE(curthread, PS_COND_WAIT);
48468516Sdeischen
485113658Sdeischen					/* Remember the CV: */
486113658Sdeischen					curthread->data.cond = *cond;
48781918Sjasone
488113658Sdeischen					/* Unlock the CV structure: */
489113658Sdeischen					THR_LOCK_RELEASE(curthread,
490113658Sdeischen					    &(*cond)->c_lock);
491113658Sdeischen
492113658Sdeischen					/* Schedule the next thread: */
493113658Sdeischen					_thr_sched_switch(curthread);
494113658Sdeischen
495113658Sdeischen					curthread->data.cond = NULL;
496114187Sdeischen					THR_UNLOCK_SWITCH(curthread);
497113658Sdeischen
49853812Salfred					/*
499113658Sdeischen					 * XXX - This really isn't a good check
500113658Sdeischen					 * since there can be more than one
501113658Sdeischen					 * thread waiting on the CV.  Signals
502113658Sdeischen					 * sent to threads waiting on mutexes
503113658Sdeischen					 * or CVs should really be deferred
504113658Sdeischen					 * until the threads are no longer
505113658Sdeischen					 * waiting, but POSIX says that signals
506113658Sdeischen					 * should be sent "as soon as possible".
50753812Salfred					 */
508113658Sdeischen					done = (seqno != (*cond)->c_seqno);
509113658Sdeischen
510113658Sdeischen					if (THR_IN_CONDQ(curthread)) {
51181918Sjasone						/*
51281918Sjasone						 * Lock the condition variable
51381918Sjasone						 * while removing the thread.
51481918Sjasone						 */
515113658Sdeischen						THR_LOCK_ACQUIRE(curthread,
516113658Sdeischen						    &(*cond)->c_lock);
51744963Sjb
51844963Sjb						cond_queue_remove(*cond,
51971581Sdeischen						    curthread);
52044963Sjb
52144963Sjb						/* Check for no more waiters: */
52244963Sjb						if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
52344963Sjb							(*cond)->c_mutex = NULL;
52444963Sjb
525113658Sdeischen						THR_LOCK_RELEASE(curthread,
526113658Sdeischen						    &(*cond)->c_lock);
527113658Sdeischen					}
52844963Sjb
529113658Sdeischen					/*
530113658Sdeischen					 * Save the interrupted flag; locking
531113658Sdeischen					 * the mutex may destroy it.
532113658Sdeischen					 */
533113658Sdeischen					interrupted = curthread->interrupted;
534113658Sdeischen					if (curthread->timeout != 0) {
535113658Sdeischen						/* The wait timedout. */
536113658Sdeischen						rval = ETIMEDOUT;
537113658Sdeischen						(void)_mutex_cv_lock(mutex);
538113658Sdeischen					} else if ((interrupted == 0) ||
539113658Sdeischen					    (done != 0))
54081918Sjasone						rval = _mutex_cv_lock(mutex);
54117706Sjulian				}
54213546Sjulian			}
54317706Sjulian			break;
54417706Sjulian
54524827Sjb		/* Trap invalid condition variable types: */
54617706Sjulian		default:
54740974Sdt			/* Unlock the condition variable structure: */
548113658Sdeischen			THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
54940974Sdt
55017706Sjulian			/* Return an invalid argument error: */
55131402Salex			rval = EINVAL;
55217706Sjulian			break;
55313546Sjulian		}
55413546Sjulian
55571581Sdeischen		if ((interrupted != 0) && (curthread->continuation != NULL))
556113658Sdeischen			curthread->continuation((void *)curthread);
55768516Sdeischen	} while ((done == 0) && (rval == 0));
55813546Sjulian
559113658Sdeischen	_thr_leave_cancellation_point(curthread);
56063364Sjasone
56113546Sjulian	/* Return the completion status: */
56213546Sjulian	return (rval);
56313546Sjulian}
56413546Sjulian
56513546Sjulianint
566113658Sdeischen__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
567113658Sdeischen		       const struct timespec *abstime)
568113658Sdeischen{
569113658Sdeischen	struct pthread *curthread = _get_curthread();
570113658Sdeischen	int ret;
571113658Sdeischen
572113658Sdeischen	_thr_enter_cancellation_point(curthread);
573113658Sdeischen	ret = _pthread_cond_timedwait(cond, mutex, abstime);
574113658Sdeischen	_thr_leave_cancellation_point(curthread);
575113658Sdeischen	return (ret);
576113658Sdeischen}
577113658Sdeischen
578113658Sdeischen
579113658Sdeischenint
58071581Sdeischen_pthread_cond_signal(pthread_cond_t * cond)
58113546Sjulian{
582113658Sdeischen	struct pthread	*curthread = _get_curthread();
583113658Sdeischen	struct pthread	*pthread;
584113658Sdeischen	int		rval = 0;
58513546Sjulian
586113870Sdeischen	THR_ASSERT(curthread->locklevel == 0,
587113870Sdeischen	    "cv_timedwait: locklevel is not zero!");
58863355Sjasone	if (cond == NULL)
58931402Salex		rval = EINVAL;
59063355Sjasone       /*
59163355Sjasone        * If the condition variable is statically initialized, perform dynamic
59263355Sjasone        * initialization.
59363355Sjasone        */
59468844Sdeischen	else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) {
59535509Sjb		/* Lock the condition variable structure: */
596113658Sdeischen		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
59713546Sjulian
59817706Sjulian		/* Process according to condition variable type: */
59917706Sjulian		switch ((*cond)->c_type) {
60024827Sjb		/* Fast condition variable: */
60117706Sjulian		case COND_TYPE_FAST:
60268516Sdeischen			/* Increment the sequence number: */
60368516Sdeischen			(*cond)->c_seqno++;
60468516Sdeischen
605113658Sdeischen			/*
606113658Sdeischen			 * Wakeups have to be done with the CV lock held;
607113658Sdeischen			 * otherwise there is a race condition where the
608113658Sdeischen			 * thread can timeout, run on another KSE, and enter
609113658Sdeischen			 * another blocking state (including blocking on a CV).
610113658Sdeischen			 */
611113658Sdeischen			if ((pthread = TAILQ_FIRST(&(*cond)->c_queue))
612113658Sdeischen			    != NULL) {
613113658Sdeischen				THR_SCHED_LOCK(curthread, pthread);
614113658Sdeischen				cond_queue_remove(*cond, pthread);
615113658Sdeischen				_thr_setrunnable_unlocked(pthread);
616113658Sdeischen				THR_SCHED_UNLOCK(curthread, pthread);
61761681Sjasone			}
61844963Sjb			/* Check for no more waiters: */
61944963Sjb			if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
62044963Sjb				(*cond)->c_mutex = NULL;
62117706Sjulian			break;
62217706Sjulian
62324827Sjb		/* Trap invalid condition variable types: */
62417706Sjulian		default:
62517706Sjulian			/* Return an invalid argument error: */
62631402Salex			rval = EINVAL;
62717706Sjulian			break;
62813546Sjulian		}
62913546Sjulian
63035509Sjb		/* Unlock the condition variable structure: */
631113658Sdeischen		THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
63213546Sjulian	}
63313546Sjulian
63413546Sjulian	/* Return the completion status: */
63513546Sjulian	return (rval);
63613546Sjulian}
63713546Sjulian
63813546Sjulianint
63971581Sdeischen_pthread_cond_broadcast(pthread_cond_t * cond)
64013546Sjulian{
641113658Sdeischen	struct pthread	*curthread = _get_curthread();
642113658Sdeischen	struct pthread	*pthread;
643113658Sdeischen	int		rval = 0;
64413546Sjulian
645113870Sdeischen	THR_ASSERT(curthread->locklevel == 0,
646113870Sdeischen	    "cv_timedwait: locklevel is not zero!");
64763355Sjasone	if (cond == NULL)
64835509Sjb		rval = EINVAL;
64963355Sjasone       /*
65063355Sjasone        * If the condition variable is statically initialized, perform dynamic
65163355Sjasone        * initialization.
65263355Sjasone        */
65368844Sdeischen	else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) {
65435509Sjb		/* Lock the condition variable structure: */
655113658Sdeischen		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
65613546Sjulian
65735509Sjb		/* Process according to condition variable type: */
65835509Sjb		switch ((*cond)->c_type) {
65935509Sjb		/* Fast condition variable: */
66035509Sjb		case COND_TYPE_FAST:
66168516Sdeischen			/* Increment the sequence number: */
66268516Sdeischen			(*cond)->c_seqno++;
66368516Sdeischen
66435509Sjb			/*
66535509Sjb			 * Enter a loop to bring all threads off the
66635509Sjb			 * condition queue:
66735509Sjb			 */
668113658Sdeischen			while ((pthread = TAILQ_FIRST(&(*cond)->c_queue))
669113658Sdeischen			    != NULL) {
670113658Sdeischen				THR_SCHED_LOCK(curthread, pthread);
671113658Sdeischen				cond_queue_remove(*cond, pthread);
672113658Sdeischen				_thr_setrunnable_unlocked(pthread);
673113658Sdeischen				THR_SCHED_UNLOCK(curthread, pthread);
67435509Sjb			}
67544963Sjb
67644963Sjb			/* There are no more waiting threads: */
67744963Sjb			(*cond)->c_mutex = NULL;
67835509Sjb			break;
67935509Sjb
68035509Sjb		/* Trap invalid condition variable types: */
68135509Sjb		default:
68235509Sjb			/* Return an invalid argument error: */
68335509Sjb			rval = EINVAL;
68435509Sjb			break;
68513546Sjulian		}
68613546Sjulian
68735509Sjb		/* Unlock the condition variable structure: */
688113658Sdeischen		THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
68913546Sjulian	}
69013546Sjulian
69113546Sjulian	/* Return the completion status: */
69213546Sjulian	return (rval);
69313546Sjulian}
69444963Sjb
69567097Sdeischenvoid
696113658Sdeischen_cond_wait_backout(struct pthread *curthread)
69767097Sdeischen{
69867097Sdeischen	pthread_cond_t	cond;
69967097Sdeischen
700113658Sdeischen	cond = curthread->data.cond;
70167097Sdeischen	if (cond != NULL) {
70267097Sdeischen		/* Lock the condition variable structure: */
703113658Sdeischen		THR_LOCK_ACQUIRE(curthread, &cond->c_lock);
70467097Sdeischen
70567097Sdeischen		/* Process according to condition variable type: */
70667097Sdeischen		switch (cond->c_type) {
70767097Sdeischen		/* Fast condition variable: */
70867097Sdeischen		case COND_TYPE_FAST:
709113658Sdeischen			cond_queue_remove(cond, curthread);
71067097Sdeischen
71167097Sdeischen			/* Check for no more waiters: */
71267097Sdeischen			if (TAILQ_FIRST(&cond->c_queue) == NULL)
71367097Sdeischen				cond->c_mutex = NULL;
71467097Sdeischen			break;
71567097Sdeischen
71667097Sdeischen		default:
71767097Sdeischen			break;
71867097Sdeischen		}
71967097Sdeischen
72067097Sdeischen		/* Unlock the condition variable structure: */
721113658Sdeischen		THR_LOCK_RELEASE(curthread, &cond->c_lock);
72267097Sdeischen	}
72367097Sdeischen}
72467097Sdeischen
72544963Sjb/*
72644963Sjb * Dequeue a waiting thread from the head of a condition queue in
72744963Sjb * descending priority order.
72844963Sjb */
729113658Sdeischenstatic inline struct pthread *
73044963Sjbcond_queue_deq(pthread_cond_t cond)
73144963Sjb{
732113658Sdeischen	struct pthread	*pthread;
73344963Sjb
73453812Salfred	while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
73567097Sdeischen		TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
736113658Sdeischen		THR_CONDQ_SET(pthread);
73753812Salfred		if ((pthread->timeout == 0) && (pthread->interrupted == 0))
73853812Salfred			/*
73953812Salfred			 * Only exit the loop when we find a thread
74053812Salfred			 * that hasn't timed out or been canceled;
74153812Salfred			 * those threads are already running and don't
74253812Salfred			 * need their run state changed.
74353812Salfred			 */
74453812Salfred			break;
74544963Sjb	}
74644963Sjb
747113658Sdeischen	return (pthread);
74844963Sjb}
74944963Sjb
75044963Sjb/*
75144963Sjb * Remove a waiting thread from a condition queue in descending priority
75244963Sjb * order.
75344963Sjb */
75444963Sjbstatic inline void
755113658Sdeischencond_queue_remove(pthread_cond_t cond, struct pthread *pthread)
75644963Sjb{
75744963Sjb	/*
75844963Sjb	 * Because pthread_cond_timedwait() can timeout as well
75944963Sjb	 * as be signaled by another thread, it is necessary to
76044963Sjb	 * guard against removing the thread from the queue if
76144963Sjb	 * it isn't in the queue.
76244963Sjb	 */
763113658Sdeischen	if (THR_IN_CONDQ(pthread)) {
76467097Sdeischen		TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
765113658Sdeischen		THR_CONDQ_CLEAR(pthread);
76644963Sjb	}
76744963Sjb}
76844963Sjb
76944963Sjb/*
77044963Sjb * Enqueue a waiting thread to a condition queue in descending priority
77144963Sjb * order.
77244963Sjb */
77344963Sjbstatic inline void
774113658Sdeischencond_queue_enq(pthread_cond_t cond, struct pthread *pthread)
77544963Sjb{
776113658Sdeischen	struct pthread *tid = TAILQ_LAST(&cond->c_queue, cond_head);
77744963Sjb
778113658Sdeischen	THR_ASSERT(!THR_IN_SYNCQ(pthread),
779113658Sdeischen	    "cond_queue_enq: thread already queued!");
78067097Sdeischen
78144963Sjb	/*
78244963Sjb	 * For the common case of all threads having equal priority,
78344963Sjb	 * we perform a quick check against the priority of the thread
78444963Sjb	 * at the tail of the queue.
78544963Sjb	 */
78644963Sjb	if ((tid == NULL) || (pthread->active_priority <= tid->active_priority))
78767097Sdeischen		TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe);
78844963Sjb	else {
78944963Sjb		tid = TAILQ_FIRST(&cond->c_queue);
79044963Sjb		while (pthread->active_priority <= tid->active_priority)
79167097Sdeischen			tid = TAILQ_NEXT(tid, sqe);
79267097Sdeischen		TAILQ_INSERT_BEFORE(tid, pthread, sqe);
79344963Sjb	}
794113658Sdeischen	THR_CONDQ_SET(pthread);
79567097Sdeischen	pthread->data.cond = cond;
79644963Sjb}
797