xref: /dflybsd-src/lib/libthread_xu/thread/thr_cond.c (revision cf8046a92768d53e67d2533fb51b137d5506248d)
171b3fa15SDavid Xu /*
271b3fa15SDavid Xu  * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
371b3fa15SDavid Xu  * All rights reserved.
471b3fa15SDavid Xu  *
571b3fa15SDavid Xu  * Redistribution and use in source and binary forms, with or without
671b3fa15SDavid Xu  * modification, are permitted provided that the following conditions
771b3fa15SDavid Xu  * are met:
871b3fa15SDavid Xu  * 1. Redistributions of source code must retain the above copyright
971b3fa15SDavid Xu  *    notice unmodified, this list of conditions, and the following
1071b3fa15SDavid Xu  *    disclaimer.
1171b3fa15SDavid Xu  * 2. Redistributions in binary form must reproduce the above copyright
1271b3fa15SDavid Xu  *    notice, this list of conditions and the following disclaimer in the
1371b3fa15SDavid Xu  *    documentation and/or other materials provided with the distribution.
1471b3fa15SDavid Xu  *
1571b3fa15SDavid Xu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1671b3fa15SDavid Xu  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1771b3fa15SDavid Xu  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1871b3fa15SDavid Xu  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1971b3fa15SDavid Xu  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2071b3fa15SDavid Xu  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2171b3fa15SDavid Xu  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2271b3fa15SDavid Xu  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2371b3fa15SDavid Xu  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2471b3fa15SDavid Xu  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2571b3fa15SDavid Xu  *
2671b3fa15SDavid Xu  */
2771b3fa15SDavid Xu 
28fc71f871SDavid Xu #include "namespace.h"
299e2ee207SJoerg Sonnenberger #include <machine/tls.h>
3071b3fa15SDavid Xu #include <stdlib.h>
3171b3fa15SDavid Xu #include <string.h>
3271b3fa15SDavid Xu #include <pthread.h>
3371b3fa15SDavid Xu #include <limits.h>
34fc71f871SDavid Xu #include "un-namespace.h"
3571b3fa15SDavid Xu 
3671b3fa15SDavid Xu #include "thr_private.h"
3771b3fa15SDavid Xu 
38fcaa7a3aSMatthew Dillon #ifdef _PTHREADS_DEBUGGING
39fcaa7a3aSMatthew Dillon #include <stdio.h>
40fcaa7a3aSMatthew Dillon #include <stdarg.h>
41fcaa7a3aSMatthew Dillon #include <sys/file.h>
42fcaa7a3aSMatthew Dillon #endif
43fcaa7a3aSMatthew Dillon 
44fcaa7a3aSMatthew Dillon #define cpu_ccfence()	__asm __volatile("" : : : "memory")
45fcaa7a3aSMatthew Dillon 
46e8382b15SDavid Xu umtx_t		_cond_static_lock;
47e8382b15SDavid Xu 
48fcaa7a3aSMatthew Dillon #ifdef _PTHREADS_DEBUGGING
49fcaa7a3aSMatthew Dillon 
50fcaa7a3aSMatthew Dillon static
51fcaa7a3aSMatthew Dillon void
cond_log(const char * ctl,...)52fcaa7a3aSMatthew Dillon cond_log(const char *ctl, ...)
53fcaa7a3aSMatthew Dillon {
54fcaa7a3aSMatthew Dillon 	char buf[256];
55fcaa7a3aSMatthew Dillon 	va_list va;
56fcaa7a3aSMatthew Dillon 	size_t len;
57fcaa7a3aSMatthew Dillon 
58fcaa7a3aSMatthew Dillon 	va_start(va, ctl);
59fcaa7a3aSMatthew Dillon 	len = vsnprintf(buf, sizeof(buf), ctl, va);
60fcaa7a3aSMatthew Dillon 	va_end(va);
61fcaa7a3aSMatthew Dillon 	_thr_log(buf, len);
62fcaa7a3aSMatthew Dillon }
63fcaa7a3aSMatthew Dillon 
64fcaa7a3aSMatthew Dillon #else
65fcaa7a3aSMatthew Dillon 
66fcaa7a3aSMatthew Dillon static __inline
67fcaa7a3aSMatthew Dillon void
cond_log(const char * ctl __unused,...)68fcaa7a3aSMatthew Dillon cond_log(const char *ctl __unused, ...)
69fcaa7a3aSMatthew Dillon {
70fcaa7a3aSMatthew Dillon }
71fcaa7a3aSMatthew Dillon 
72fcaa7a3aSMatthew Dillon #endif
73fcaa7a3aSMatthew Dillon 
7471b3fa15SDavid Xu /*
7571b3fa15SDavid Xu  * Prototypes
7671b3fa15SDavid Xu  */
7719451dc5Szrj int	__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
7819451dc5Szrj int	__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
7919451dc5Szrj 				 const struct timespec *abstime);
8071b3fa15SDavid Xu static int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
8171b3fa15SDavid Xu 			    const struct timespec *abstime, int cancel);
8271b3fa15SDavid Xu static int cond_signal_common(pthread_cond_t *cond, int broadcast);
8371b3fa15SDavid Xu 
8471b3fa15SDavid Xu static int
cond_init(pthread_cond_t * cond,const pthread_condattr_t * cond_attr)8571b3fa15SDavid Xu cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
8671b3fa15SDavid Xu {
8771b3fa15SDavid Xu 	pthread_cond_t pcond;
8871b3fa15SDavid Xu 	int rval = 0;
8971b3fa15SDavid Xu 
90*cf8046a9Szrj 	pcond = __malloc(sizeof(struct __pthread_cond_s));
91e7bf3f77SMatthew Dillon 	if (pcond == NULL) {
9271b3fa15SDavid Xu 		rval = ENOMEM;
9371b3fa15SDavid Xu 	} else {
9471b3fa15SDavid Xu 		/*
9571b3fa15SDavid Xu 		 * Initialise the condition variable structure:
9671b3fa15SDavid Xu 		 */
9771b3fa15SDavid Xu 		_thr_umtx_init(&pcond->c_lock);
9871b3fa15SDavid Xu 		if (cond_attr == NULL || *cond_attr == NULL) {
9971b3fa15SDavid Xu 			pcond->c_pshared = 0;
10071b3fa15SDavid Xu 			pcond->c_clockid = CLOCK_REALTIME;
10171b3fa15SDavid Xu 		} else {
10271b3fa15SDavid Xu 			pcond->c_pshared = (*cond_attr)->c_pshared;
10371b3fa15SDavid Xu 			pcond->c_clockid = (*cond_attr)->c_clockid;
10471b3fa15SDavid Xu 		}
105fcaa7a3aSMatthew Dillon 		TAILQ_INIT(&pcond->c_waitlist);
10671b3fa15SDavid Xu 		*cond = pcond;
10771b3fa15SDavid Xu 	}
10871b3fa15SDavid Xu 	/* Return the completion status: */
10971b3fa15SDavid Xu 	return (rval);
11071b3fa15SDavid Xu }
11171b3fa15SDavid Xu 
112fcaa7a3aSMatthew Dillon #if 0
113fcaa7a3aSMatthew Dillon void
114fcaa7a3aSMatthew Dillon _cond_reinit(pthread_cond_t cond)
115fcaa7a3aSMatthew Dillon {
116fcaa7a3aSMatthew Dillon 	if (cond) {
117fcaa7a3aSMatthew Dillon 		_thr_umtx_init(&cond->c_lock);
118fcaa7a3aSMatthew Dillon #if 0
119fcaa7a3aSMatthew Dillon 		/* retain state */
120fcaa7a3aSMatthew Dillon 		cond->c_pshared = 0;
121fcaa7a3aSMatthew Dillon 		cond->c_clockid = CLOCK_REALTIME;
122fcaa7a3aSMatthew Dillon #endif
123fcaa7a3aSMatthew Dillon 		TAILQ_INIT(&cond->c_waitlist);
124fcaa7a3aSMatthew Dillon 	}
125fcaa7a3aSMatthew Dillon }
126fcaa7a3aSMatthew Dillon #endif
127fcaa7a3aSMatthew Dillon 
12871b3fa15SDavid Xu static int
init_static(pthread_t thread,pthread_cond_t * cond)129940be950Szrj init_static(pthread_t thread, pthread_cond_t *cond)
13071b3fa15SDavid Xu {
13171b3fa15SDavid Xu 	int ret;
13271b3fa15SDavid Xu 
13371b3fa15SDavid Xu 	THR_LOCK_ACQUIRE(thread, &_cond_static_lock);
13471b3fa15SDavid Xu 
13571b3fa15SDavid Xu 	if (*cond == NULL)
13671b3fa15SDavid Xu 		ret = cond_init(cond, NULL);
13771b3fa15SDavid Xu 	else
13871b3fa15SDavid Xu 		ret = 0;
13971b3fa15SDavid Xu 
14071b3fa15SDavid Xu 	THR_LOCK_RELEASE(thread, &_cond_static_lock);
14171b3fa15SDavid Xu 
14271b3fa15SDavid Xu 	return (ret);
14371b3fa15SDavid Xu }
14471b3fa15SDavid Xu 
14571b3fa15SDavid Xu int
_pthread_cond_init(pthread_cond_t * __restrict cond,const pthread_condattr_t * __restrict cond_attr)146d33005aaSSascha Wildner _pthread_cond_init(pthread_cond_t * __restrict cond,
147d33005aaSSascha Wildner     const pthread_condattr_t * __restrict cond_attr)
14871b3fa15SDavid Xu {
14971b3fa15SDavid Xu 	*cond = NULL;
15071b3fa15SDavid Xu 	return cond_init(cond, cond_attr);
15171b3fa15SDavid Xu }
15271b3fa15SDavid Xu 
15371b3fa15SDavid Xu int
_pthread_cond_destroy(pthread_cond_t * cond)15471b3fa15SDavid Xu _pthread_cond_destroy(pthread_cond_t *cond)
15571b3fa15SDavid Xu {
156940be950Szrj 	pthread_cond_t	cv;
157940be950Szrj 	pthread_t	curthread = tls_get_curthread();
15871b3fa15SDavid Xu 	int		rval = 0;
15971b3fa15SDavid Xu 
160e7bf3f77SMatthew Dillon 	if (cond == NULL) {
16171b3fa15SDavid Xu 		rval = EINVAL;
162e7bf3f77SMatthew Dillon 	} else if (*cond == NULL) {
163146da5fcSMichael Neumann 		rval = 0;
164e7bf3f77SMatthew Dillon 	} else {
16571b3fa15SDavid Xu 		/* Lock the condition variable structure: */
16671b3fa15SDavid Xu 		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
167fcaa7a3aSMatthew Dillon 		if (TAILQ_FIRST(&(*cond)->c_waitlist)) {
16871b3fa15SDavid Xu 			THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
16971b3fa15SDavid Xu 			return (EBUSY);
17071b3fa15SDavid Xu 		}
17171b3fa15SDavid Xu 
17271b3fa15SDavid Xu 		/*
17371b3fa15SDavid Xu 		 * NULL the caller's pointer now that the condition
17471b3fa15SDavid Xu 		 * variable has been destroyed:
17571b3fa15SDavid Xu 		 */
17671b3fa15SDavid Xu 		cv = *cond;
17771b3fa15SDavid Xu 		*cond = NULL;
17871b3fa15SDavid Xu 
17971b3fa15SDavid Xu 		/* Unlock the condition variable structure: */
18071b3fa15SDavid Xu 		THR_LOCK_RELEASE(curthread, &cv->c_lock);
18171b3fa15SDavid Xu 
18271b3fa15SDavid Xu 		/* Free the cond lock structure: */
18371b3fa15SDavid Xu 
18471b3fa15SDavid Xu 		/*
18571b3fa15SDavid Xu 		 * Free the memory allocated for the condition
18671b3fa15SDavid Xu 		 * variable structure:
18771b3fa15SDavid Xu 		 */
188e7bf3f77SMatthew Dillon 		__free(cv);
18971b3fa15SDavid Xu 
19071b3fa15SDavid Xu 	}
19171b3fa15SDavid Xu 	/* Return the completion status: */
19271b3fa15SDavid Xu 	return (rval);
19371b3fa15SDavid Xu }
19471b3fa15SDavid Xu 
195fcaa7a3aSMatthew Dillon struct cond_cancel_info {
196fcaa7a3aSMatthew Dillon 	TAILQ_ENTRY(cond_cancel_info) entry;
19771b3fa15SDavid Xu 	pthread_mutex_t	*mutex;
19871b3fa15SDavid Xu 	pthread_cond_t	*cond;
199a8851a0fSDavid Xu 	int		count;
200fcaa7a3aSMatthew Dillon 	int		queued;
20171b3fa15SDavid Xu };
20271b3fa15SDavid Xu 
20371b3fa15SDavid Xu static void
cond_cancel_handler(void * arg)20471b3fa15SDavid Xu cond_cancel_handler(void *arg)
20571b3fa15SDavid Xu {
206940be950Szrj 	pthread_t curthread = tls_get_curthread();
207a8851a0fSDavid Xu 	struct cond_cancel_info *info = (struct cond_cancel_info *)arg;
20871b3fa15SDavid Xu 	pthread_cond_t cv;
20971b3fa15SDavid Xu 
210fcaa7a3aSMatthew Dillon 	cv = *info->cond;
21171b3fa15SDavid Xu 	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
212fcaa7a3aSMatthew Dillon 	cond_log("cond_cancel %p\n", cv);
213fcaa7a3aSMatthew Dillon 
214fcaa7a3aSMatthew Dillon 	if (info->queued) {
215fcaa7a3aSMatthew Dillon 		info->queued = 0;
216fcaa7a3aSMatthew Dillon 		cond_log("cond_cancel %p: info %p\n", cv, info);
217fcaa7a3aSMatthew Dillon 		TAILQ_REMOVE(&cv->c_waitlist, info, entry);
218fcaa7a3aSMatthew Dillon 		_thr_umtx_wake(&info->queued, 0);
21971b3fa15SDavid Xu 	}
22071b3fa15SDavid Xu 	THR_LOCK_RELEASE(curthread, &cv->c_lock);
22171b3fa15SDavid Xu 
222fcaa7a3aSMatthew Dillon 	/* _mutex_cv_lock(info->mutex, info->count); */
22371b3fa15SDavid Xu }
22471b3fa15SDavid Xu 
225fcaa7a3aSMatthew Dillon /*
226fcaa7a3aSMatthew Dillon  * Wait for pthread_cond_t to be signaled.
227fcaa7a3aSMatthew Dillon  *
228fcaa7a3aSMatthew Dillon  * NOTE: EINTR is ignored and may not be returned by this function.
229fcaa7a3aSMatthew Dillon  */
23071b3fa15SDavid Xu static int
cond_wait_common(pthread_cond_t * cond,pthread_mutex_t * mutex,const struct timespec * abstime,int cancel)23171b3fa15SDavid Xu cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
23271b3fa15SDavid Xu 		 const struct timespec *abstime, int cancel)
23371b3fa15SDavid Xu {
234940be950Szrj 	pthread_t curthread = tls_get_curthread();
23571b3fa15SDavid Xu 	struct timespec ts, ts2, *tsp;
236a8851a0fSDavid Xu 	struct cond_cancel_info info;
23771b3fa15SDavid Xu 	pthread_cond_t  cv;
23871b3fa15SDavid Xu 	int		oldcancel;
239fcaa7a3aSMatthew Dillon 	int		ret;
24071b3fa15SDavid Xu 
24171b3fa15SDavid Xu 	/*
24271b3fa15SDavid Xu 	 * If the condition variable is statically initialized,
24371b3fa15SDavid Xu 	 * perform the dynamic initialization:
24471b3fa15SDavid Xu 	 */
245fcaa7a3aSMatthew Dillon 	cond_log("cond_wait_common %p on mutex %p info %p\n",
246fcaa7a3aSMatthew Dillon 		*cond, *mutex, &info);
24771b3fa15SDavid Xu 	if (__predict_false(*cond == NULL &&
248fcaa7a3aSMatthew Dillon 	    (ret = init_static(curthread, cond)) != 0)) {
249fcaa7a3aSMatthew Dillon 		cond_log("cond_wait_common %p (failedA %d)\n", *cond, ret);
25071b3fa15SDavid Xu 		return (ret);
25171b3fa15SDavid Xu 	}
252fcaa7a3aSMatthew Dillon 
253fcaa7a3aSMatthew Dillon 	cv = *cond;
254fcaa7a3aSMatthew Dillon 	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
255fcaa7a3aSMatthew Dillon 	ret = _mutex_cv_unlock(mutex, &info.count);
256fcaa7a3aSMatthew Dillon 	if (ret) {
257fcaa7a3aSMatthew Dillon 		cond_log("cond_wait_common %p (failedB %d)\n", cv, ret);
258fcaa7a3aSMatthew Dillon 		THR_LOCK_RELEASE(curthread, &cv->c_lock);
259fcaa7a3aSMatthew Dillon 		return ret;
260fcaa7a3aSMatthew Dillon 	}
261fcaa7a3aSMatthew Dillon 
262fcaa7a3aSMatthew Dillon 	cpu_ccfence();
263a8851a0fSDavid Xu 	info.mutex = mutex;
264a8851a0fSDavid Xu 	info.cond  = cond;
265fcaa7a3aSMatthew Dillon 	info.queued = 1;
266fcaa7a3aSMatthew Dillon 	TAILQ_INSERT_TAIL(&cv->c_waitlist, &info, entry);
2673db51647SMatthew Dillon 
2683db51647SMatthew Dillon 	/*
2693db51647SMatthew Dillon 	 * loop if we have never been told to wake up
2703db51647SMatthew Dillon 	 * or we lost a race.
2713db51647SMatthew Dillon 	 */
272fcaa7a3aSMatthew Dillon 	while (info.queued) {
27371b3fa15SDavid Xu 		THR_LOCK_RELEASE(curthread, &cv->c_lock);
27471b3fa15SDavid Xu 
27571b3fa15SDavid Xu 		if (abstime != NULL) {
27671b3fa15SDavid Xu 			clock_gettime(cv->c_clockid, &ts);
277ce96aca2SSascha Wildner 			timespecsub(abstime, &ts, &ts2);
27871b3fa15SDavid Xu 			tsp = &ts2;
279fcaa7a3aSMatthew Dillon 		} else {
28071b3fa15SDavid Xu 			tsp = NULL;
281fcaa7a3aSMatthew Dillon 		}
28271b3fa15SDavid Xu 
28371b3fa15SDavid Xu 		if (cancel) {
284a8851a0fSDavid Xu 			THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info);
28571b3fa15SDavid Xu 			oldcancel = _thr_cancel_enter(curthread);
286fcaa7a3aSMatthew Dillon 			ret = _thr_umtx_wait(&info.queued, 1, tsp,
2879219c44cSDavid Xu 					     cv->c_clockid);
28871b3fa15SDavid Xu 			_thr_cancel_leave(curthread, oldcancel);
28971b3fa15SDavid Xu 			THR_CLEANUP_POP(curthread, 0);
29071b3fa15SDavid Xu 		} else {
291fcaa7a3aSMatthew Dillon 			ret = _thr_umtx_wait(&info.queued, 1, tsp,
2929219c44cSDavid Xu 					     cv->c_clockid);
29371b3fa15SDavid Xu 		}
29471b3fa15SDavid Xu 
295fcaa7a3aSMatthew Dillon 		/*
296fcaa7a3aSMatthew Dillon 		 * Ignore EINTR.  Make sure ret is 0 if not ETIMEDOUT.
297fcaa7a3aSMatthew Dillon 		 */
29871b3fa15SDavid Xu 		THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
29971b3fa15SDavid Xu 		if (abstime != NULL && ret == ETIMEDOUT)
30071b3fa15SDavid Xu 			break;
301fcaa7a3aSMatthew Dillon 		cpu_ccfence();
30271b3fa15SDavid Xu 	}
303fcaa7a3aSMatthew Dillon 
304fcaa7a3aSMatthew Dillon 	if (info.queued) {
305fcaa7a3aSMatthew Dillon 		info.queued = 0;
306fcaa7a3aSMatthew Dillon 		TAILQ_REMOVE(&cv->c_waitlist, &info, entry);
307fcaa7a3aSMatthew Dillon 		ret = ETIMEDOUT;
308fcaa7a3aSMatthew Dillon 	} else {
3093db51647SMatthew Dillon 		ret = 0;
310fcaa7a3aSMatthew Dillon 	}
31171b3fa15SDavid Xu 	THR_LOCK_RELEASE(curthread, &cv->c_lock);
312fcaa7a3aSMatthew Dillon 
313fcaa7a3aSMatthew Dillon 	cond_log("cond_wait_common %p (doneA)\n", cv);
314a8851a0fSDavid Xu 	_mutex_cv_lock(mutex, info.count);
315fcaa7a3aSMatthew Dillon 
316fcaa7a3aSMatthew Dillon 	if (ret)
317fcaa7a3aSMatthew Dillon 		cond_log("cond_wait_common %p (failed %d)\n", cv, ret);
318fcaa7a3aSMatthew Dillon 	else
319fcaa7a3aSMatthew Dillon 		cond_log("cond_wait_common %p (doneB)\n", cv);
320fcaa7a3aSMatthew Dillon 
32171b3fa15SDavid Xu 	return (ret);
32271b3fa15SDavid Xu }
32371b3fa15SDavid Xu 
32471b3fa15SDavid Xu int
_pthread_cond_wait(pthread_cond_t * cond,pthread_mutex_t * mutex)32571b3fa15SDavid Xu _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
32671b3fa15SDavid Xu {
32736750816SDavid Xu 	return (cond_wait_common(cond, mutex, NULL, 0));
32871b3fa15SDavid Xu }
32971b3fa15SDavid Xu 
33071b3fa15SDavid Xu int
__pthread_cond_wait(pthread_cond_t * __restrict cond,pthread_mutex_t * __restrict mutex)331d33005aaSSascha Wildner __pthread_cond_wait(pthread_cond_t * __restrict cond,
332d33005aaSSascha Wildner     pthread_mutex_t * __restrict mutex)
33371b3fa15SDavid Xu {
33436750816SDavid Xu 	return (cond_wait_common(cond, mutex, NULL, 1));
33571b3fa15SDavid Xu }
33671b3fa15SDavid Xu 
33771b3fa15SDavid Xu int
_pthread_cond_timedwait(pthread_cond_t * __restrict cond,pthread_mutex_t * __restrict mutex,const struct timespec * __restrict abstime)338d33005aaSSascha Wildner _pthread_cond_timedwait(pthread_cond_t * __restrict cond,
339d33005aaSSascha Wildner     pthread_mutex_t * __restrict mutex,
340d33005aaSSascha Wildner     const struct timespec * __restrict abstime)
34171b3fa15SDavid Xu {
34271b3fa15SDavid Xu 	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
34371b3fa15SDavid Xu 	    abstime->tv_nsec >= 1000000000)
34471b3fa15SDavid Xu 		return (EINVAL);
34571b3fa15SDavid Xu 
34636750816SDavid Xu 	return (cond_wait_common(cond, mutex, abstime, 0));
34771b3fa15SDavid Xu }
34871b3fa15SDavid Xu 
34971b3fa15SDavid Xu int
__pthread_cond_timedwait(pthread_cond_t * cond,pthread_mutex_t * mutex,const struct timespec * abstime)35071b3fa15SDavid Xu __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
35171b3fa15SDavid Xu 		       const struct timespec *abstime)
35271b3fa15SDavid Xu {
35371b3fa15SDavid Xu 	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
35471b3fa15SDavid Xu 	    abstime->tv_nsec >= 1000000000)
35571b3fa15SDavid Xu 		return (EINVAL);
35671b3fa15SDavid Xu 
35736750816SDavid Xu 	return (cond_wait_common(cond, mutex, abstime, 1));
35871b3fa15SDavid Xu }
35971b3fa15SDavid Xu 
36071b3fa15SDavid Xu static int
cond_signal_common(pthread_cond_t * cond,int broadcast)36171b3fa15SDavid Xu cond_signal_common(pthread_cond_t *cond, int broadcast)
36271b3fa15SDavid Xu {
363940be950Szrj 	pthread_t	curthread = tls_get_curthread();
364fcaa7a3aSMatthew Dillon 	struct cond_cancel_info *info;
36571b3fa15SDavid Xu 	pthread_cond_t	cv;
36671b3fa15SDavid Xu 	int		ret = 0;
36771b3fa15SDavid Xu 
368fcaa7a3aSMatthew Dillon 	cond_log("cond_signal_common %p broad=%d\n", *cond, broadcast);
369fcaa7a3aSMatthew Dillon 
37071b3fa15SDavid Xu 	/*
37171b3fa15SDavid Xu 	 * If the condition variable is statically initialized, perform dynamic
37271b3fa15SDavid Xu 	 * initialization.
37371b3fa15SDavid Xu 	 */
37471b3fa15SDavid Xu 	if (__predict_false(*cond == NULL &&
375fcaa7a3aSMatthew Dillon 			    (ret = init_static(curthread, cond)) != 0)) {
376fcaa7a3aSMatthew Dillon 		cond_log("cond_signal_common %p (failedA %d)\n", *cond, ret);
37771b3fa15SDavid Xu 		return (ret);
378fcaa7a3aSMatthew Dillon 	}
37971b3fa15SDavid Xu 
38071b3fa15SDavid Xu 	cv = *cond;
38171b3fa15SDavid Xu 	/* Lock the condition variable structure. */
38271b3fa15SDavid Xu 	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
383fcaa7a3aSMatthew Dillon 	while ((info = TAILQ_FIRST(&cv->c_waitlist)) != NULL) {
384fcaa7a3aSMatthew Dillon 		info->queued = 0;
385fcaa7a3aSMatthew Dillon 		TAILQ_REMOVE(&cv->c_waitlist, info, entry);
386fcaa7a3aSMatthew Dillon 		cond_log("cond_signal_common %p: wakeup %p\n", *cond, info);
387fcaa7a3aSMatthew Dillon 		_thr_umtx_wake(&info->queued, 0);
388fcaa7a3aSMatthew Dillon 		if (broadcast == 0)
389fcaa7a3aSMatthew Dillon 			break;
39071b3fa15SDavid Xu 	}
39171b3fa15SDavid Xu 	THR_LOCK_RELEASE(curthread, &cv->c_lock);
392fcaa7a3aSMatthew Dillon 
393fcaa7a3aSMatthew Dillon 	if (ret)
394fcaa7a3aSMatthew Dillon 		cond_log("cond_signal_common %p (failedB %d)\n", *cond, ret);
395fcaa7a3aSMatthew Dillon 	else
396fcaa7a3aSMatthew Dillon 		cond_log("cond_signal_common %p (done)\n", *cond);
397fcaa7a3aSMatthew Dillon 
39871b3fa15SDavid Xu 	return (ret);
39971b3fa15SDavid Xu }
40071b3fa15SDavid Xu 
40171b3fa15SDavid Xu int
_pthread_cond_signal(pthread_cond_t * cond)40271b3fa15SDavid Xu _pthread_cond_signal(pthread_cond_t * cond)
40371b3fa15SDavid Xu {
40436750816SDavid Xu 	return (cond_signal_common(cond, 0));
40571b3fa15SDavid Xu }
40671b3fa15SDavid Xu 
40771b3fa15SDavid Xu int
_pthread_cond_broadcast(pthread_cond_t * cond)40871b3fa15SDavid Xu _pthread_cond_broadcast(pthread_cond_t * cond)
40971b3fa15SDavid Xu {
41036750816SDavid Xu 	return (cond_signal_common(cond, 1));
41171b3fa15SDavid Xu }
4125a1048c8SDavid Xu 
4135a1048c8SDavid Xu /*
4145a1048c8SDavid Xu  * Double underscore versions are cancellation points.  Single underscore
4155a1048c8SDavid Xu  * versions are not and are provided for libc internal usage (which
4165a1048c8SDavid Xu  * shouldn't introduce cancellation points).
4175a1048c8SDavid Xu  */
4185a1048c8SDavid Xu __strong_reference(__pthread_cond_wait, pthread_cond_wait);
4195a1048c8SDavid Xu __strong_reference(__pthread_cond_timedwait, pthread_cond_timedwait);
4205a1048c8SDavid Xu 
4215a1048c8SDavid Xu __strong_reference(_pthread_cond_init, pthread_cond_init);
4225a1048c8SDavid Xu __strong_reference(_pthread_cond_destroy, pthread_cond_destroy);
4235a1048c8SDavid Xu __strong_reference(_pthread_cond_signal, pthread_cond_signal);
4245a1048c8SDavid Xu __strong_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
425