thr_cond.c revision 139023
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 139023 2004-12-18 18:07:37Z 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_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);
50139023Sdeischenstatic void			cond_wait_backout(void *);
51139023Sdeischenstatic inline void		check_continuation(struct pthread *,
52139023Sdeischen				    struct pthread_cond *, pthread_mutex_t *);
5344963Sjb
54115173Sdeischen/*
55115173Sdeischen * Double underscore versions are cancellation points.  Single underscore
56115173Sdeischen * versions are not and are provided for libc internal usage (which
57115173Sdeischen * shouldn't introduce cancellation points).
58115173Sdeischen */
59115173Sdeischen__weak_reference(__pthread_cond_wait, pthread_cond_wait);
60115173Sdeischen__weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait);
61115173Sdeischen
6275369Sdeischen__weak_reference(_pthread_cond_init, pthread_cond_init);
6375369Sdeischen__weak_reference(_pthread_cond_destroy, pthread_cond_destroy);
6475369Sdeischen__weak_reference(_pthread_cond_signal, pthread_cond_signal);
6575369Sdeischen__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
6671581Sdeischen
6771581Sdeischen
6848046Sjbint
6971581Sdeischen_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
7013546Sjulian{
7113546Sjulian	enum pthread_cond_type type;
7217706Sjulian	pthread_cond_t	pcond;
73113658Sdeischen	int		flags;
7413546Sjulian	int             rval = 0;
7513546Sjulian
7635509Sjb	if (cond == NULL)
7731402Salex		rval = EINVAL;
7835509Sjb	else {
7917706Sjulian		/*
8024827Sjb		 * Check if a pointer to a condition variable attribute
8124827Sjb		 * structure was passed by the caller:
8217706Sjulian		 */
8317706Sjulian		if (cond_attr != NULL && *cond_attr != NULL) {
8417706Sjulian			/* Default to a fast condition variable: */
8517706Sjulian			type = (*cond_attr)->c_type;
86113658Sdeischen			flags = (*cond_attr)->c_flags;
8717706Sjulian		} else {
8817706Sjulian			/* Default to a fast condition variable: */
8917706Sjulian			type = COND_TYPE_FAST;
90113658Sdeischen			flags = 0;
9117706Sjulian		}
9213546Sjulian
9317706Sjulian		/* Process according to condition variable type: */
9417706Sjulian		switch (type) {
9524827Sjb		/* Fast condition variable: */
9617706Sjulian		case COND_TYPE_FAST:
9717706Sjulian			/* Nothing to do here. */
9817706Sjulian			break;
9913546Sjulian
10024827Sjb		/* Trap invalid condition variable types: */
10117706Sjulian		default:
10217706Sjulian			/* Return an invalid argument error: */
10331402Salex			rval = EINVAL;
10417706Sjulian			break;
10517706Sjulian		}
10613546Sjulian
10717706Sjulian		/* Check for no errors: */
10817706Sjulian		if (rval == 0) {
10924827Sjb			if ((pcond = (pthread_cond_t)
11024827Sjb			    malloc(sizeof(struct pthread_cond))) == NULL) {
11131402Salex				rval = ENOMEM;
112113658Sdeischen			} else if (_lock_init(&pcond->c_lock, LCK_ADAPTIVE,
113113786Sdeischen			    _thr_lock_wait, _thr_lock_wakeup) != 0) {
114113658Sdeischen				free(pcond);
115113658Sdeischen				rval = ENOMEM;
11617706Sjulian			} else {
11717706Sjulian				/*
11817706Sjulian				 * Initialise the condition variable
11917706Sjulian				 * structure:
12017706Sjulian				 */
12144963Sjb				TAILQ_INIT(&pcond->c_queue);
122120069Sdavidxu				pcond->c_flags = COND_FLAGS_INITED;
12317706Sjulian				pcond->c_type = type;
12444963Sjb				pcond->c_mutex = NULL;
12568516Sdeischen				pcond->c_seqno = 0;
12617706Sjulian				*cond = pcond;
12717706Sjulian			}
12817706Sjulian		}
12913546Sjulian	}
13013546Sjulian	/* Return the completion status: */
13113546Sjulian	return (rval);
13213546Sjulian}
13313546Sjulian
13413546Sjulianint
13571581Sdeischen_pthread_cond_destroy(pthread_cond_t *cond)
13613546Sjulian{
137113658Sdeischen	struct pthread_cond	*cv;
138113658Sdeischen	struct pthread		*curthread = _get_curthread();
139113658Sdeischen	int			rval = 0;
14013546Sjulian
14135509Sjb	if (cond == NULL || *cond == NULL)
14231402Salex		rval = EINVAL;
14335509Sjb	else {
14435509Sjb		/* Lock the condition variable structure: */
145113658Sdeischen		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
14613546Sjulian
14735509Sjb		/*
148113658Sdeischen		 * NULL the caller's pointer now that the condition
149113658Sdeischen		 * variable has been destroyed:
150113658Sdeischen		 */
151113658Sdeischen		cv = *cond;
152113658Sdeischen		*cond = NULL;
153113658Sdeischen
154113658Sdeischen		/* Unlock the condition variable structure: */
155113658Sdeischen		THR_LOCK_RELEASE(curthread, &cv->c_lock);
156113658Sdeischen
157115761Sdavidxu		/* Free the cond lock structure: */
158115761Sdavidxu		_lock_destroy(&cv->c_lock);
159115761Sdavidxu
160113658Sdeischen		/*
16135509Sjb		 * Free the memory allocated for the condition
16235509Sjb		 * variable structure:
16335509Sjb		 */
164113658Sdeischen		free(cv);
16517706Sjulian
16613546Sjulian	}
16713546Sjulian	/* Return the completion status: */
16813546Sjulian	return (rval);
16913546Sjulian}
17013546Sjulian
17113546Sjulianint
17271581Sdeischen_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
17313546Sjulian{
17471581Sdeischen	struct pthread	*curthread = _get_curthread();
17556277Sjasone	int	rval = 0;
17668516Sdeischen	int	done = 0;
177139023Sdeischen	int	mutex_locked = 1;
17868516Sdeischen	int	seqno;
17913546Sjulian
180115173Sdeischen	if (cond == NULL)
18168516Sdeischen		return (EINVAL);
18235027Sjb
18335027Sjb	/*
18435027Sjb	 * If the condition variable is statically initialized,
18535027Sjb	 * perform the dynamic initialization:
18635027Sjb	 */
18768516Sdeischen	if (*cond == NULL &&
188115173Sdeischen	    (rval = pthread_cond_init(cond, NULL)) != 0)
18968516Sdeischen		return (rval);
19068516Sdeischen
191115278Sdeischen	if (!_kse_isthreaded())
192115278Sdeischen		_kse_setthreaded(1);
193115278Sdeischen
19468516Sdeischen	/*
19568516Sdeischen	 * Enter a loop waiting for a condition signal or broadcast
19668516Sdeischen	 * to wake up this thread.  A loop is needed in case the waiting
19768516Sdeischen	 * thread is interrupted by a signal to execute a signal handler.
19868516Sdeischen	 * It is not (currently) possible to remain in the waiting queue
19968516Sdeischen	 * while running a handler.  Instead, the thread is interrupted
20068516Sdeischen	 * and backed out of the waiting queue prior to executing the
20168516Sdeischen	 * signal handler.
20268516Sdeischen	 */
203139023Sdeischen
204139023Sdeischen	/* Lock the condition variable structure: */
205139023Sdeischen	THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
206139023Sdeischen	seqno = (*cond)->c_seqno;
20768516Sdeischen	do {
20847424Sjb		/*
20947424Sjb		 * If the condvar was statically allocated, properly
21047424Sjb		 * initialize the tail queue.
21147424Sjb		 */
21247424Sjb		if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
21347424Sjb			TAILQ_INIT(&(*cond)->c_queue);
21447424Sjb			(*cond)->c_flags |= COND_FLAGS_INITED;
21547424Sjb		}
21647424Sjb
21717706Sjulian		/* Process according to condition variable type: */
21817706Sjulian		switch ((*cond)->c_type) {
21924827Sjb		/* Fast condition variable: */
22017706Sjulian		case COND_TYPE_FAST:
22144963Sjb			if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
22244963Sjb			    ((*cond)->c_mutex != *mutex))) {
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
23644963Sjb				/* Wait forever: */
23771581Sdeischen				curthread->wakeup_time.tv_sec = -1;
23844963Sjb
23944963Sjb				/* Unlock the mutex: */
240139023Sdeischen				if (mutex_locked &&
241113658Sdeischen				    ((rval = _mutex_cv_unlock(mutex)) != 0)) {
24244963Sjb					/*
24344963Sjb					 * Cannot unlock the mutex, so remove
24444963Sjb					 * the running thread from the condition
24544963Sjb					 * variable queue:
24644963Sjb					 */
24771581Sdeischen					cond_queue_remove(*cond, curthread);
248113658Sdeischen				}
249113658Sdeischen				else {
250139023Sdeischen					/* Remember the mutex: */
251139023Sdeischen					(*cond)->c_mutex = *mutex;
252139023Sdeischen
25344963Sjb					/*
254113658Sdeischen					 * Don't unlock the mutex the next
255113658Sdeischen					 * time through the loop (if the
256113658Sdeischen					 * thread has to be requeued after
257113658Sdeischen					 * handling a signal).
25844963Sjb					 */
259139023Sdeischen					mutex_locked = 0;
26044963Sjb
261113658Sdeischen					/*
262113658Sdeischen					 * This thread is active and is in a
263113658Sdeischen					 * critical region (holding the cv
264113658Sdeischen					 * lock); we should be able to safely
265113658Sdeischen					 * set the state.
266113658Sdeischen					 */
267115080Sdeischen					THR_SCHED_LOCK(curthread, curthread);
268113658Sdeischen					THR_SET_STATE(curthread, PS_COND_WAIT);
26956277Sjasone
270113658Sdeischen					/* Remember the CV: */
271113658Sdeischen					curthread->data.cond = *cond;
272139023Sdeischen					curthread->sigbackout = cond_wait_backout;
273115080Sdeischen					THR_SCHED_UNLOCK(curthread, curthread);
27481918Sjasone
275113658Sdeischen					/* Unlock the CV structure: */
276113658Sdeischen					THR_LOCK_RELEASE(curthread,
277113658Sdeischen					    &(*cond)->c_lock);
278113658Sdeischen
279113658Sdeischen					/* Schedule the next thread: */
280113658Sdeischen					_thr_sched_switch(curthread);
281113658Sdeischen
28281918Sjasone					/*
283113658Sdeischen					 * XXX - This really isn't a good check
284113658Sdeischen					 * since there can be more than one
285113658Sdeischen					 * thread waiting on the CV.  Signals
286113658Sdeischen					 * sent to threads waiting on mutexes
287113658Sdeischen					 * or CVs should really be deferred
288113658Sdeischen					 * until the threads are no longer
289113658Sdeischen					 * waiting, but POSIX says that signals
290113658Sdeischen					 * should be sent "as soon as possible".
29181918Sjasone					 */
292113658Sdeischen					done = (seqno != (*cond)->c_seqno);
293139023Sdeischen					if (done && !THR_IN_CONDQ(curthread)) {
29456277Sjasone						/*
295139023Sdeischen						 * The thread is dequeued, so
296139023Sdeischen						 * it is safe to clear these.
29753812Salfred						 */
298139023Sdeischen						curthread->data.cond = NULL;
299139023Sdeischen						curthread->sigbackout = NULL;
300139023Sdeischen						check_continuation(curthread,
301139023Sdeischen						    NULL, mutex);
302139023Sdeischen						return (_mutex_cv_lock(mutex));
303139023Sdeischen					}
30453812Salfred
305139023Sdeischen					/* Relock the CV structure: */
306139023Sdeischen					THR_LOCK_ACQUIRE(curthread,
307139023Sdeischen					    &(*cond)->c_lock);
308139023Sdeischen
309139023Sdeischen					/*
310139023Sdeischen					 * Clear these after taking the lock to
311139023Sdeischen					 * prevent a race condition where a
312139023Sdeischen					 * signal can arrive before dequeueing
313139023Sdeischen					 * the thread.
314139023Sdeischen					 */
315139023Sdeischen					curthread->data.cond = NULL;
316139023Sdeischen					curthread->sigbackout = NULL;
317139023Sdeischen					done = (seqno != (*cond)->c_seqno);
318139023Sdeischen
319139023Sdeischen					if (THR_IN_CONDQ(curthread)) {
32053812Salfred						cond_queue_remove(*cond,
32171581Sdeischen						    curthread);
32253812Salfred
32353812Salfred						/* Check for no more waiters: */
32453812Salfred						if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
32553812Salfred							(*cond)->c_mutex = NULL;
326113658Sdeischen					}
32744963Sjb				}
32840974Sdt			}
32917706Sjulian			break;
33013546Sjulian
33124827Sjb		/* Trap invalid condition variable types: */
33217706Sjulian		default:
33317706Sjulian			/* Return an invalid argument error: */
33431402Salex			rval = EINVAL;
33517706Sjulian			break;
33617706Sjulian		}
33753812Salfred
338139023Sdeischen		check_continuation(curthread, *cond,
339139023Sdeischen		    mutex_locked ? NULL : mutex);
34068516Sdeischen	} while ((done == 0) && (rval == 0));
34113546Sjulian
342139023Sdeischen	/* Unlock the condition variable structure: */
343139023Sdeischen	THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
344139023Sdeischen
345139023Sdeischen	if (mutex_locked == 0)
346139023Sdeischen		_mutex_cv_lock(mutex);
347139023Sdeischen
34813546Sjulian	/* Return the completion status: */
34913546Sjulian	return (rval);
35013546Sjulian}
35113546Sjulian
352115399Skan__strong_reference(_pthread_cond_wait, _thr_cond_wait);
353115399Skan
35413546Sjulianint
355113658Sdeischen__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
356113658Sdeischen{
357113658Sdeischen	struct pthread *curthread = _get_curthread();
358113658Sdeischen	int ret;
359113658Sdeischen
360123312Sdavidxu	_thr_cancel_enter(curthread);
361113658Sdeischen	ret = _pthread_cond_wait(cond, mutex);
362123312Sdavidxu	_thr_cancel_leave(curthread, 1);
363113658Sdeischen	return (ret);
364113658Sdeischen}
365113658Sdeischen
366113658Sdeischenint
36771581Sdeischen_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
36813546Sjulian		       const struct timespec * abstime)
36913546Sjulian{
37071581Sdeischen	struct pthread	*curthread = _get_curthread();
37156277Sjasone	int	rval = 0;
37268516Sdeischen	int	done = 0;
373139023Sdeischen	int	mutex_locked = 1;
37468516Sdeischen	int	seqno;
37513546Sjulian
376113870Sdeischen	THR_ASSERT(curthread->locklevel == 0,
377113870Sdeischen	    "cv_timedwait: locklevel is not zero!");
378113658Sdeischen
37963355Sjasone	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
380115173Sdeischen	    abstime->tv_nsec >= 1000000000)
38168516Sdeischen		return (EINVAL);
38235027Sjb	/*
38363355Sjasone	 * If the condition variable is statically initialized, perform dynamic
38463355Sjasone	 * initialization.
38535027Sjb	 */
386115173Sdeischen	if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0)
38768516Sdeischen		return (rval);
38868516Sdeischen
389115278Sdeischen	if (!_kse_isthreaded())
390115278Sdeischen		_kse_setthreaded(1);
391115278Sdeischen
39268516Sdeischen	/*
39368516Sdeischen	 * Enter a loop waiting for a condition signal or broadcast
39468516Sdeischen	 * to wake up this thread.  A loop is needed in case the waiting
39568516Sdeischen	 * thread is interrupted by a signal to execute a signal handler.
39668516Sdeischen	 * It is not (currently) possible to remain in the waiting queue
39768516Sdeischen	 * while running a handler.  Instead, the thread is interrupted
39868516Sdeischen	 * and backed out of the waiting queue prior to executing the
39968516Sdeischen	 * signal handler.
40068516Sdeischen	 */
401139023Sdeischen
402139023Sdeischen	/* Lock the condition variable structure: */
403139023Sdeischen	THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
404139023Sdeischen	seqno = (*cond)->c_seqno;
40568516Sdeischen	do {
40647424Sjb		/*
40747424Sjb		 * If the condvar was statically allocated, properly
40847424Sjb		 * initialize the tail queue.
40947424Sjb		 */
41047424Sjb		if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
41147424Sjb			TAILQ_INIT(&(*cond)->c_queue);
41247424Sjb			(*cond)->c_flags |= COND_FLAGS_INITED;
41347424Sjb		}
41447424Sjb
41517706Sjulian		/* Process according to condition variable type: */
41617706Sjulian		switch ((*cond)->c_type) {
41724827Sjb		/* Fast condition variable: */
41817706Sjulian		case COND_TYPE_FAST:
41944963Sjb			if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
42044963Sjb			    ((*cond)->c_mutex != *mutex))) {
42144963Sjb				/* Return invalid argument error: */
42244963Sjb				rval = EINVAL;
42344963Sjb			} else {
42444963Sjb				/* Set the wakeup time: */
425113658Sdeischen				curthread->wakeup_time.tv_sec = abstime->tv_sec;
42671581Sdeischen				curthread->wakeup_time.tv_nsec =
42744963Sjb				    abstime->tv_nsec;
42813546Sjulian
42953812Salfred				/* Reset the timeout and interrupted flags: */
43071581Sdeischen				curthread->timeout = 0;
43171581Sdeischen				curthread->interrupted = 0;
43213546Sjulian
43317706Sjulian				/*
43444963Sjb				 * Queue the running thread for the condition
43544963Sjb				 * variable:
43617706Sjulian				 */
43771581Sdeischen				cond_queue_enq(*cond, curthread);
43840974Sdt
43944963Sjb				/* Unlock the mutex: */
440139023Sdeischen				if (mutex_locked &&
441113658Sdeischen				   ((rval = _mutex_cv_unlock(mutex)) != 0)) {
44244963Sjb					/*
443113658Sdeischen					 * Cannot unlock the mutex; remove the
444113658Sdeischen					 * running thread from the condition
44544963Sjb					 * variable queue:
44644963Sjb					 */
44771581Sdeischen					cond_queue_remove(*cond, curthread);
448139023Sdeischen				} else {
449139023Sdeischen					/* Remember the mutex: */
450139023Sdeischen					(*cond)->c_mutex = *mutex;
45144963Sjb
45244963Sjb					/*
453113658Sdeischen					 * Don't unlock the mutex the next
454113658Sdeischen					 * time through the loop (if the
455113658Sdeischen					 * thread has to be requeued after
456113658Sdeischen					 * handling a signal).
45744963Sjb					 */
458139023Sdeischen					mutex_locked = 0;
45944963Sjb
460113658Sdeischen					/*
461113658Sdeischen					 * This thread is active and is in a
462113658Sdeischen					 * critical region (holding the cv
463113658Sdeischen					 * lock); we should be able to safely
464113658Sdeischen					 * set the state.
465113658Sdeischen					 */
466115080Sdeischen					THR_SCHED_LOCK(curthread, curthread);
467113658Sdeischen					THR_SET_STATE(curthread, PS_COND_WAIT);
46868516Sdeischen
469113658Sdeischen					/* Remember the CV: */
470113658Sdeischen					curthread->data.cond = *cond;
471139023Sdeischen					curthread->sigbackout = cond_wait_backout;
472115080Sdeischen					THR_SCHED_UNLOCK(curthread, curthread);
47381918Sjasone
474113658Sdeischen					/* Unlock the CV structure: */
475113658Sdeischen					THR_LOCK_RELEASE(curthread,
476113658Sdeischen					    &(*cond)->c_lock);
477113658Sdeischen
478113658Sdeischen					/* Schedule the next thread: */
479113658Sdeischen					_thr_sched_switch(curthread);
480113658Sdeischen
48153812Salfred					/*
482113658Sdeischen					 * XXX - This really isn't a good check
483113658Sdeischen					 * since there can be more than one
484113658Sdeischen					 * thread waiting on the CV.  Signals
485113658Sdeischen					 * sent to threads waiting on mutexes
486113658Sdeischen					 * or CVs should really be deferred
487113658Sdeischen					 * until the threads are no longer
488113658Sdeischen					 * waiting, but POSIX says that signals
489113658Sdeischen					 * should be sent "as soon as possible".
49053812Salfred					 */
491113658Sdeischen					done = (seqno != (*cond)->c_seqno);
492139023Sdeischen					if (done && !THR_IN_CONDQ(curthread)) {
49381918Sjasone						/*
494139023Sdeischen						 * The thread is dequeued, so
495139023Sdeischen						 * it is safe to clear these.
49681918Sjasone						 */
497139023Sdeischen						curthread->data.cond = NULL;
498139023Sdeischen						curthread->sigbackout = NULL;
499139023Sdeischen						check_continuation(curthread,
500139023Sdeischen						    NULL, mutex);
501139023Sdeischen						return (_mutex_cv_lock(mutex));
502139023Sdeischen					}
50344963Sjb
504139023Sdeischen					/* Relock the CV structure: */
505139023Sdeischen					THR_LOCK_ACQUIRE(curthread,
506139023Sdeischen					    &(*cond)->c_lock);
507139023Sdeischen
508139023Sdeischen					/*
509139023Sdeischen					 * Clear these after taking the lock to
510139023Sdeischen					 * prevent a race condition where a
511139023Sdeischen					 * signal can arrive before dequeueing
512139023Sdeischen					 * the thread.
513139023Sdeischen					 */
514139023Sdeischen					curthread->data.cond = NULL;
515139023Sdeischen					curthread->sigbackout = NULL;
516139023Sdeischen
517139023Sdeischen					done = (seqno != (*cond)->c_seqno);
518139023Sdeischen
519139023Sdeischen					if (THR_IN_CONDQ(curthread)) {
52044963Sjb						cond_queue_remove(*cond,
52171581Sdeischen						    curthread);
52244963Sjb
52344963Sjb						/* Check for no more waiters: */
52444963Sjb						if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
52544963Sjb							(*cond)->c_mutex = NULL;
526113658Sdeischen					}
52744963Sjb
528113658Sdeischen					if (curthread->timeout != 0) {
529113658Sdeischen						/* The wait timedout. */
530113658Sdeischen						rval = ETIMEDOUT;
531117165Sdavidxu					}
53217706Sjulian				}
53313546Sjulian			}
53417706Sjulian			break;
53517706Sjulian
53624827Sjb		/* Trap invalid condition variable types: */
53717706Sjulian		default:
53817706Sjulian			/* Return an invalid argument error: */
53931402Salex			rval = EINVAL;
54017706Sjulian			break;
54113546Sjulian		}
54213546Sjulian
543139023Sdeischen		check_continuation(curthread, *cond,
544139023Sdeischen		    mutex_locked ? NULL : mutex);
54568516Sdeischen	} while ((done == 0) && (rval == 0));
54613546Sjulian
547139023Sdeischen	/* Unlock the condition variable structure: */
548139023Sdeischen	THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
549139023Sdeischen
550139023Sdeischen	if (mutex_locked == 0)
551139023Sdeischen		_mutex_cv_lock(mutex);
552139023Sdeischen
55313546Sjulian	/* Return the completion status: */
55413546Sjulian	return (rval);
55513546Sjulian}
55613546Sjulian
55713546Sjulianint
558113658Sdeischen__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
559113658Sdeischen		       const struct timespec *abstime)
560113658Sdeischen{
561113658Sdeischen	struct pthread *curthread = _get_curthread();
562113658Sdeischen	int ret;
563113658Sdeischen
564123312Sdavidxu	_thr_cancel_enter(curthread);
565113658Sdeischen	ret = _pthread_cond_timedwait(cond, mutex, abstime);
566123312Sdavidxu	_thr_cancel_leave(curthread, 1);
567113658Sdeischen	return (ret);
568113658Sdeischen}
569113658Sdeischen
570113658Sdeischen
571113658Sdeischenint
57271581Sdeischen_pthread_cond_signal(pthread_cond_t * cond)
57313546Sjulian{
574113658Sdeischen	struct pthread	*curthread = _get_curthread();
575113658Sdeischen	struct pthread	*pthread;
576117907Sdeischen	struct kse_mailbox *kmbx;
577113658Sdeischen	int		rval = 0;
57813546Sjulian
579113870Sdeischen	THR_ASSERT(curthread->locklevel == 0,
580113870Sdeischen	    "cv_timedwait: locklevel is not zero!");
58163355Sjasone	if (cond == NULL)
58231402Salex		rval = EINVAL;
58363355Sjasone       /*
58463355Sjasone        * If the condition variable is statically initialized, perform dynamic
58563355Sjasone        * initialization.
58663355Sjasone        */
58768844Sdeischen	else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) {
58835509Sjb		/* Lock the condition variable structure: */
589113658Sdeischen		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
59013546Sjulian
59117706Sjulian		/* Process according to condition variable type: */
59217706Sjulian		switch ((*cond)->c_type) {
59324827Sjb		/* Fast condition variable: */
59417706Sjulian		case COND_TYPE_FAST:
59568516Sdeischen			/* Increment the sequence number: */
59668516Sdeischen			(*cond)->c_seqno++;
59768516Sdeischen
598113658Sdeischen			/*
599113658Sdeischen			 * Wakeups have to be done with the CV lock held;
600113658Sdeischen			 * otherwise there is a race condition where the
601113658Sdeischen			 * thread can timeout, run on another KSE, and enter
602113658Sdeischen			 * another blocking state (including blocking on a CV).
603113658Sdeischen			 */
604113658Sdeischen			if ((pthread = TAILQ_FIRST(&(*cond)->c_queue))
605113658Sdeischen			    != NULL) {
606113658Sdeischen				THR_SCHED_LOCK(curthread, pthread);
607113658Sdeischen				cond_queue_remove(*cond, pthread);
608139023Sdeischen				pthread->sigbackout = NULL;
609117714Sdeischen				if ((pthread->kseg == curthread->kseg) &&
610117714Sdeischen				    (pthread->active_priority >
611117714Sdeischen				    curthread->active_priority))
612117714Sdeischen					curthread->critical_yield = 1;
613117907Sdeischen				kmbx = _thr_setrunnable_unlocked(pthread);
614113658Sdeischen				THR_SCHED_UNLOCK(curthread, pthread);
615117907Sdeischen				if (kmbx != NULL)
616117907Sdeischen					kse_wakeup(kmbx);
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
638115399Skan__strong_reference(_pthread_cond_signal, _thr_cond_signal);
639115399Skan
64013546Sjulianint
64171581Sdeischen_pthread_cond_broadcast(pthread_cond_t * cond)
64213546Sjulian{
643113658Sdeischen	struct pthread	*curthread = _get_curthread();
644113658Sdeischen	struct pthread	*pthread;
645117907Sdeischen	struct kse_mailbox *kmbx;
646113658Sdeischen	int		rval = 0;
64713546Sjulian
648113870Sdeischen	THR_ASSERT(curthread->locklevel == 0,
649113870Sdeischen	    "cv_timedwait: locklevel is not zero!");
65063355Sjasone	if (cond == NULL)
65135509Sjb		rval = EINVAL;
65263355Sjasone       /*
65363355Sjasone        * If the condition variable is statically initialized, perform dynamic
65463355Sjasone        * initialization.
65563355Sjasone        */
65668844Sdeischen	else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) {
65735509Sjb		/* Lock the condition variable structure: */
658113658Sdeischen		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
65913546Sjulian
66035509Sjb		/* Process according to condition variable type: */
66135509Sjb		switch ((*cond)->c_type) {
66235509Sjb		/* Fast condition variable: */
66335509Sjb		case COND_TYPE_FAST:
66468516Sdeischen			/* Increment the sequence number: */
66568516Sdeischen			(*cond)->c_seqno++;
66668516Sdeischen
66735509Sjb			/*
66835509Sjb			 * Enter a loop to bring all threads off the
66935509Sjb			 * condition queue:
67035509Sjb			 */
671113658Sdeischen			while ((pthread = TAILQ_FIRST(&(*cond)->c_queue))
672113658Sdeischen			    != NULL) {
673113658Sdeischen				THR_SCHED_LOCK(curthread, pthread);
674113658Sdeischen				cond_queue_remove(*cond, pthread);
675139023Sdeischen				pthread->sigbackout = NULL;
676117714Sdeischen				if ((pthread->kseg == curthread->kseg) &&
677117714Sdeischen				    (pthread->active_priority >
678117714Sdeischen				    curthread->active_priority))
679117714Sdeischen					curthread->critical_yield = 1;
680117907Sdeischen				kmbx = _thr_setrunnable_unlocked(pthread);
681113658Sdeischen				THR_SCHED_UNLOCK(curthread, pthread);
682117907Sdeischen				if (kmbx != NULL)
683117907Sdeischen					kse_wakeup(kmbx);
68435509Sjb			}
68544963Sjb
68644963Sjb			/* There are no more waiting threads: */
68744963Sjb			(*cond)->c_mutex = NULL;
68835509Sjb			break;
689115399Skan
69035509Sjb		/* Trap invalid condition variable types: */
69135509Sjb		default:
69235509Sjb			/* Return an invalid argument error: */
69335509Sjb			rval = EINVAL;
69435509Sjb			break;
69513546Sjulian		}
69613546Sjulian
69735509Sjb		/* Unlock the condition variable structure: */
698113658Sdeischen		THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
69913546Sjulian	}
70013546Sjulian
70113546Sjulian	/* Return the completion status: */
70213546Sjulian	return (rval);
70313546Sjulian}
70444963Sjb
705115399Skan__strong_reference(_pthread_cond_broadcast, _thr_cond_broadcast);
706115399Skan
707139023Sdeischenstatic inline void
708139023Sdeischencheck_continuation(struct pthread *curthread, struct pthread_cond *cond,
709139023Sdeischen    pthread_mutex_t *mutex)
71067097Sdeischen{
711139023Sdeischen	if ((curthread->interrupted != 0) &&
712139023Sdeischen	    (curthread->continuation != NULL)) {
713139023Sdeischen		if (cond != NULL)
714139023Sdeischen			/* Unlock the condition variable structure: */
715139023Sdeischen			THR_LOCK_RELEASE(curthread, &cond->c_lock);
716139023Sdeischen		/*
717139023Sdeischen		 * Note that even though this thread may have been
718139023Sdeischen		 * canceled, POSIX requires that the mutex be
719139023Sdeischen		 * reaquired prior to cancellation.
720139023Sdeischen		 */
721139023Sdeischen		if (mutex != NULL)
722139023Sdeischen			_mutex_cv_lock(mutex);
723139023Sdeischen		curthread->continuation((void *) curthread);
724139023Sdeischen		PANIC("continuation returned in pthread_cond_wait.\n");
725139023Sdeischen	}
726139023Sdeischen}
727139023Sdeischen
728139023Sdeischenstatic void
729139023Sdeischencond_wait_backout(void *arg)
730139023Sdeischen{
731139023Sdeischen	struct pthread *curthread = (struct pthread *)arg;
73267097Sdeischen	pthread_cond_t	cond;
73367097Sdeischen
734113658Sdeischen	cond = curthread->data.cond;
73567097Sdeischen	if (cond != NULL) {
73667097Sdeischen		/* Lock the condition variable structure: */
737113658Sdeischen		THR_LOCK_ACQUIRE(curthread, &cond->c_lock);
73867097Sdeischen
73967097Sdeischen		/* Process according to condition variable type: */
74067097Sdeischen		switch (cond->c_type) {
74167097Sdeischen		/* Fast condition variable: */
74267097Sdeischen		case COND_TYPE_FAST:
743113658Sdeischen			cond_queue_remove(cond, curthread);
74467097Sdeischen
74567097Sdeischen			/* Check for no more waiters: */
74667097Sdeischen			if (TAILQ_FIRST(&cond->c_queue) == NULL)
74767097Sdeischen				cond->c_mutex = NULL;
74867097Sdeischen			break;
74967097Sdeischen
75067097Sdeischen		default:
75167097Sdeischen			break;
75267097Sdeischen		}
75367097Sdeischen
75467097Sdeischen		/* Unlock the condition variable structure: */
755113658Sdeischen		THR_LOCK_RELEASE(curthread, &cond->c_lock);
75667097Sdeischen	}
757139023Sdeischen	/* No need to call this again. */
758139023Sdeischen	curthread->sigbackout = NULL;
75967097Sdeischen}
76067097Sdeischen
76144963Sjb/*
76244963Sjb * Dequeue a waiting thread from the head of a condition queue in
76344963Sjb * descending priority order.
76444963Sjb */
765113658Sdeischenstatic inline struct pthread *
76644963Sjbcond_queue_deq(pthread_cond_t cond)
76744963Sjb{
768113658Sdeischen	struct pthread	*pthread;
76944963Sjb
77053812Salfred	while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
77167097Sdeischen		TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
772117165Sdavidxu		THR_CONDQ_CLEAR(pthread);
77353812Salfred		if ((pthread->timeout == 0) && (pthread->interrupted == 0))
77453812Salfred			/*
77553812Salfred			 * Only exit the loop when we find a thread
77653812Salfred			 * that hasn't timed out or been canceled;
77753812Salfred			 * those threads are already running and don't
77853812Salfred			 * need their run state changed.
77953812Salfred			 */
78053812Salfred			break;
78144963Sjb	}
78244963Sjb
783113658Sdeischen	return (pthread);
78444963Sjb}
78544963Sjb
78644963Sjb/*
78744963Sjb * Remove a waiting thread from a condition queue in descending priority
78844963Sjb * order.
78944963Sjb */
79044963Sjbstatic inline void
791113658Sdeischencond_queue_remove(pthread_cond_t cond, struct pthread *pthread)
79244963Sjb{
79344963Sjb	/*
79444963Sjb	 * Because pthread_cond_timedwait() can timeout as well
79544963Sjb	 * as be signaled by another thread, it is necessary to
79644963Sjb	 * guard against removing the thread from the queue if
79744963Sjb	 * it isn't in the queue.
79844963Sjb	 */
799113658Sdeischen	if (THR_IN_CONDQ(pthread)) {
80067097Sdeischen		TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
801113658Sdeischen		THR_CONDQ_CLEAR(pthread);
80244963Sjb	}
80344963Sjb}
80444963Sjb
80544963Sjb/*
80644963Sjb * Enqueue a waiting thread to a condition queue in descending priority
80744963Sjb * order.
80844963Sjb */
80944963Sjbstatic inline void
810113658Sdeischencond_queue_enq(pthread_cond_t cond, struct pthread *pthread)
81144963Sjb{
812113658Sdeischen	struct pthread *tid = TAILQ_LAST(&cond->c_queue, cond_head);
81344963Sjb
814113658Sdeischen	THR_ASSERT(!THR_IN_SYNCQ(pthread),
815113658Sdeischen	    "cond_queue_enq: thread already queued!");
81667097Sdeischen
81744963Sjb	/*
81844963Sjb	 * For the common case of all threads having equal priority,
81944963Sjb	 * we perform a quick check against the priority of the thread
82044963Sjb	 * at the tail of the queue.
82144963Sjb	 */
82244963Sjb	if ((tid == NULL) || (pthread->active_priority <= tid->active_priority))
82367097Sdeischen		TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe);
82444963Sjb	else {
82544963Sjb		tid = TAILQ_FIRST(&cond->c_queue);
82644963Sjb		while (pthread->active_priority <= tid->active_priority)
82767097Sdeischen			tid = TAILQ_NEXT(tid, sqe);
82867097Sdeischen		TAILQ_INSERT_BEFORE(tid, pthread, sqe);
82944963Sjb	}
830113658Sdeischen	THR_CONDQ_SET(pthread);
83167097Sdeischen	pthread->data.cond = cond;
83244963Sjb}
833