xref: /freebsd/lib/libthr/thread/thr_cond.c (revision a224a3919d4b2260bb1d91fb7f848de673444c26)
1bb535300SJeff Roberson /*
2bb535300SJeff Roberson  * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
3bb535300SJeff Roberson  * All rights reserved.
4bb535300SJeff Roberson  *
5bb535300SJeff Roberson  * Redistribution and use in source and binary forms, with or without
6bb535300SJeff Roberson  * modification, are permitted provided that the following conditions
7bb535300SJeff Roberson  * are met:
8bb535300SJeff Roberson  * 1. Redistributions of source code must retain the above copyright
9bb535300SJeff Roberson  *    notice, this list of conditions and the following disclaimer.
10bb535300SJeff Roberson  * 2. Redistributions in binary form must reproduce the above copyright
11bb535300SJeff Roberson  *    notice, this list of conditions and the following disclaimer in the
12bb535300SJeff Roberson  *    documentation and/or other materials provided with the distribution.
13bb535300SJeff Roberson  * 3. All advertising materials mentioning features or use of this software
14bb535300SJeff Roberson  *    must display the following acknowledgement:
15bb535300SJeff Roberson  *	This product includes software developed by John Birrell.
16bb535300SJeff Roberson  * 4. Neither the name of the author nor the names of any co-contributors
17bb535300SJeff Roberson  *    may be used to endorse or promote products derived from this software
18bb535300SJeff Roberson  *    without specific prior written permission.
19bb535300SJeff Roberson  *
20bb535300SJeff Roberson  * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
21bb535300SJeff Roberson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22bb535300SJeff Roberson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23bb535300SJeff Roberson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24bb535300SJeff Roberson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25bb535300SJeff Roberson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26bb535300SJeff Roberson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27bb535300SJeff Roberson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28bb535300SJeff Roberson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29bb535300SJeff Roberson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30bb535300SJeff Roberson  * SUCH DAMAGE.
31bb535300SJeff Roberson  *
32bb535300SJeff Roberson  * $FreeBSD$
33bb535300SJeff Roberson  */
34bb535300SJeff Roberson #include <stdlib.h>
35bb535300SJeff Roberson #include <errno.h>
36bb535300SJeff Roberson #include <string.h>
37bb535300SJeff Roberson #include <pthread.h>
38bb535300SJeff Roberson #include "thr_private.h"
39bb535300SJeff Roberson 
40bb535300SJeff Roberson /*
41bb535300SJeff Roberson  * Prototypes
42bb535300SJeff Roberson  */
43bb535300SJeff Roberson static pthread_t	cond_queue_deq(pthread_cond_t);
44bb535300SJeff Roberson static void		cond_queue_remove(pthread_cond_t, pthread_t);
45bb535300SJeff Roberson static void		cond_queue_enq(pthread_cond_t, pthread_t);
46a224a391SMike Makonnen static int		cond_signal(pthread_cond_t *, int);
47dd3b229eSMike Makonnen static int		cond_wait_common(pthread_cond_t *,
48dd3b229eSMike Makonnen 			    pthread_mutex_t *, const struct timespec *);
49bb535300SJeff Roberson 
50bb535300SJeff Roberson __weak_reference(_pthread_cond_init, pthread_cond_init);
51bb535300SJeff Roberson __weak_reference(_pthread_cond_destroy, pthread_cond_destroy);
52bb535300SJeff Roberson __weak_reference(_pthread_cond_wait, pthread_cond_wait);
53bb535300SJeff Roberson __weak_reference(_pthread_cond_timedwait, pthread_cond_timedwait);
54bb535300SJeff Roberson __weak_reference(_pthread_cond_signal, pthread_cond_signal);
55bb535300SJeff Roberson __weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
56bb535300SJeff Roberson 
57bb535300SJeff Roberson #define	COND_LOCK(c)						\
58bb535300SJeff Roberson do {								\
59bb535300SJeff Roberson 	if (umtx_lock(&(c)->c_lock, curthread->thr_id))		\
60bb535300SJeff Roberson 		abort();					\
61bb535300SJeff Roberson } while (0)
62bb535300SJeff Roberson 
63bb535300SJeff Roberson #define	COND_UNLOCK(c)						\
64bb535300SJeff Roberson do {								\
65bb535300SJeff Roberson 	if (umtx_unlock(&(c)->c_lock, curthread->thr_id))	\
66bb535300SJeff Roberson 		abort();					\
67bb535300SJeff Roberson } while (0)
68bb535300SJeff Roberson 
69bb535300SJeff Roberson 
70bb535300SJeff Roberson /* Reinitialize a condition variable to defaults. */
71bb535300SJeff Roberson int
72bb535300SJeff Roberson _cond_reinit(pthread_cond_t *cond)
73bb535300SJeff Roberson {
74bb535300SJeff Roberson 	if (cond == NULL)
75bb535300SJeff Roberson 		return (EINVAL);
76bb535300SJeff Roberson 
77bb535300SJeff Roberson 	if (*cond == NULL)
78bb535300SJeff Roberson 		return (pthread_cond_init(cond, NULL));
79bb535300SJeff Roberson 
80bb535300SJeff Roberson 	/*
81bb535300SJeff Roberson 	 * Initialize the condition variable structure:
82bb535300SJeff Roberson 	 */
83bb535300SJeff Roberson 	TAILQ_INIT(&(*cond)->c_queue);
84bb535300SJeff Roberson 	(*cond)->c_flags = COND_FLAGS_INITED;
85bb535300SJeff Roberson 	(*cond)->c_type = COND_TYPE_FAST;
86bb535300SJeff Roberson 	(*cond)->c_mutex = NULL;
87bb535300SJeff Roberson 	(*cond)->c_seqno = 0;
88bb535300SJeff Roberson 	bzero(&(*cond)->c_lock, sizeof((*cond)->c_lock));
89bb535300SJeff Roberson 
90bb535300SJeff Roberson 	return (0);
91bb535300SJeff Roberson }
92bb535300SJeff Roberson 
93bb535300SJeff Roberson int
94bb535300SJeff Roberson _pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
95bb535300SJeff Roberson {
96bb535300SJeff Roberson 	enum pthread_cond_type type;
97bb535300SJeff Roberson 	pthread_cond_t	pcond;
98bb535300SJeff Roberson 
99bb535300SJeff Roberson 	if (cond == NULL)
100bb535300SJeff Roberson 		return (EINVAL);
101bb535300SJeff Roberson 
102bb535300SJeff Roberson 	/*
103bb535300SJeff Roberson 	 * Check if a pointer to a condition variable attribute
104bb535300SJeff Roberson 	 * structure was passed by the caller:
105bb535300SJeff Roberson 	 */
106bb535300SJeff Roberson 	if (cond_attr != NULL && *cond_attr != NULL)
107bb535300SJeff Roberson 		type = (*cond_attr)->c_type;
108bb535300SJeff Roberson 	else
109bb535300SJeff Roberson 		/* Default to a fast condition variable: */
110bb535300SJeff Roberson 		type = COND_TYPE_FAST;
111bb535300SJeff Roberson 
112bb535300SJeff Roberson 	/* Process according to condition variable type: */
113bb535300SJeff Roberson 	switch (type) {
114bb535300SJeff Roberson 	case COND_TYPE_FAST:
115bb535300SJeff Roberson 		break;
116bb535300SJeff Roberson 	default:
117bb535300SJeff Roberson 		return (EINVAL);
118bb535300SJeff Roberson 		break;
119bb535300SJeff Roberson 	}
120bb535300SJeff Roberson 
121bb535300SJeff Roberson 	if ((pcond = (pthread_cond_t)
122bb535300SJeff Roberson 	    malloc(sizeof(struct pthread_cond))) == NULL)
123bb535300SJeff Roberson 		return (ENOMEM);
124bb535300SJeff Roberson 	/*
125bb535300SJeff Roberson 	 * Initialise the condition variable
126bb535300SJeff Roberson 	 * structure:
127bb535300SJeff Roberson 	 */
128bb535300SJeff Roberson 	TAILQ_INIT(&pcond->c_queue);
129bb535300SJeff Roberson 	pcond->c_flags |= COND_FLAGS_INITED;
130bb535300SJeff Roberson 	pcond->c_type = type;
131bb535300SJeff Roberson 	pcond->c_mutex = NULL;
132bb535300SJeff Roberson 	pcond->c_seqno = 0;
133bb535300SJeff Roberson 	bzero(&pcond->c_lock, sizeof(pcond->c_lock));
134bb535300SJeff Roberson 
135bb535300SJeff Roberson 	*cond = pcond;
136bb535300SJeff Roberson 
137bb535300SJeff Roberson 	return (0);
138bb535300SJeff Roberson }
139bb535300SJeff Roberson 
140bb535300SJeff Roberson int
141bb535300SJeff Roberson _pthread_cond_destroy(pthread_cond_t *cond)
142bb535300SJeff Roberson {
143bb535300SJeff Roberson 	if (cond == NULL || *cond == NULL)
144bb535300SJeff Roberson 		return (EINVAL);
145bb535300SJeff Roberson 
146bb535300SJeff Roberson 	COND_LOCK(*cond);
147bb535300SJeff Roberson 
148bb535300SJeff Roberson 	/*
149bb535300SJeff Roberson 	 * Free the memory allocated for the condition
150bb535300SJeff Roberson 	 * variable structure:
151bb535300SJeff Roberson 	 */
152bb535300SJeff Roberson 	free(*cond);
153bb535300SJeff Roberson 
154bb535300SJeff Roberson 	/*
155bb535300SJeff Roberson 	 * NULL the caller's pointer now that the condition
156bb535300SJeff Roberson 	 * variable has been destroyed:
157bb535300SJeff Roberson 	 */
158bb535300SJeff Roberson 	*cond = NULL;
159bb535300SJeff Roberson 
160bb535300SJeff Roberson 	return (0);
161bb535300SJeff Roberson }
162bb535300SJeff Roberson 
163bb535300SJeff Roberson int
164bb535300SJeff Roberson _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
165bb535300SJeff Roberson {
166bb535300SJeff Roberson 	int rval;
167bb535300SJeff Roberson 
168dd3b229eSMike Makonnen 	rval = cond_wait_common(cond, mutex, NULL);
169bb535300SJeff Roberson 
170bb535300SJeff Roberson 	/* This should never happen. */
171bb535300SJeff Roberson 	if (rval == ETIMEDOUT)
172bb535300SJeff Roberson 		abort();
173bb535300SJeff Roberson 
174bb535300SJeff Roberson 	return (rval);
175bb535300SJeff Roberson }
176bb535300SJeff Roberson 
177bb535300SJeff Roberson int
178bb535300SJeff Roberson _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
179bb535300SJeff Roberson 		       const struct timespec * abstime)
180bb535300SJeff Roberson {
181dd3b229eSMike Makonnen 	if (abstime == NULL || abstime->tv_nsec >= 1000000000)
182dd3b229eSMike Makonnen 		return (EINVAL);
183dd3b229eSMike Makonnen 
184dd3b229eSMike Makonnen 	return (cond_wait_common(cond, mutex, abstime));
185dd3b229eSMike Makonnen }
186dd3b229eSMike Makonnen 
187dd3b229eSMike Makonnen static int
188dd3b229eSMike Makonnen cond_wait_common(pthread_cond_t * cond, pthread_mutex_t * mutex,
189dd3b229eSMike Makonnen 	         const struct timespec * abstime)
190dd3b229eSMike Makonnen {
191bb535300SJeff Roberson 	int	rval = 0;
192bb535300SJeff Roberson 	int	done = 0;
193bb535300SJeff Roberson 	int	seqno;
194bb535300SJeff Roberson 	int	mtxrval;
195bb535300SJeff Roberson 
196bb535300SJeff Roberson 
197bb535300SJeff Roberson 	_thread_enter_cancellation_point();
198bb535300SJeff Roberson 
199a224a391SMike Makonnen 	if (cond == NULL)
200a224a391SMike Makonnen 		return (EINVAL);
201bb535300SJeff Roberson 	/*
202bb535300SJeff Roberson 	 * If the condition variable is statically initialized, perform dynamic
203bb535300SJeff Roberson 	 * initialization.
204bb535300SJeff Roberson 	 */
205bb535300SJeff Roberson 	if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0)
206bb535300SJeff Roberson 		return (rval);
207bb535300SJeff Roberson 
208bb535300SJeff Roberson 
209bb535300SJeff Roberson 	COND_LOCK(*cond);
210bb535300SJeff Roberson 
211bb535300SJeff Roberson 	/*
212bb535300SJeff Roberson 	 * If the condvar was statically allocated, properly
213bb535300SJeff Roberson 	 * initialize the tail queue.
214bb535300SJeff Roberson 	 */
215bb535300SJeff Roberson 	if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
216bb535300SJeff Roberson 		TAILQ_INIT(&(*cond)->c_queue);
217bb535300SJeff Roberson 		(*cond)->c_flags |= COND_FLAGS_INITED;
218bb535300SJeff Roberson 	}
219bb535300SJeff Roberson 
220bb535300SJeff Roberson 	/* Process according to condition variable type. */
221bb535300SJeff Roberson 
222bb535300SJeff Roberson 	switch ((*cond)->c_type) {
223bb535300SJeff Roberson 	/* Fast condition variable: */
224bb535300SJeff Roberson 	case COND_TYPE_FAST:
225bb535300SJeff Roberson 		if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
226bb535300SJeff Roberson 		    ((*cond)->c_mutex != *mutex))) {
227bb535300SJeff Roberson 			COND_UNLOCK(*cond);
228bb535300SJeff Roberson 			rval = EINVAL;
229bb535300SJeff Roberson 			break;
230bb535300SJeff Roberson 		}
231bb535300SJeff Roberson 		/* Remember the mutex */
232bb535300SJeff Roberson 		(*cond)->c_mutex = *mutex;
233bb535300SJeff Roberson 
234bb535300SJeff Roberson 		if ((rval = _mutex_cv_unlock(mutex)) != 0) {
235bb535300SJeff Roberson 			if (rval == -1){
236bb535300SJeff Roberson 				printf("foo");
237bb535300SJeff Roberson 				fflush(stdout);
238bb535300SJeff Roberson 				abort();
239bb535300SJeff Roberson 			}
240bb535300SJeff Roberson 
241bb535300SJeff Roberson 			COND_UNLOCK(*cond);
242bb535300SJeff Roberson 			break;
243bb535300SJeff Roberson 		}
244bb535300SJeff Roberson 
245bb535300SJeff Roberson 		/*
246a224a391SMike Makonnen 		 * We need to protect the queue operations.  It also
247a224a391SMike Makonnen 		 * protects c_seqno and the pthread flag fields.  This is
248a224a391SMike Makonnen 		 * dropped before calling _thread_suspend() and reaquired
249a224a391SMike Makonnen 		 * when we return.
250bb535300SJeff Roberson 		 */
251bb535300SJeff Roberson 
252a224a391SMike Makonnen 		_thread_critical_enter(curthread);
253bb535300SJeff Roberson 		/*
254a224a391SMike Makonnen 		 * c_seqno is protected.
255bb535300SJeff Roberson 		 */
256bb535300SJeff Roberson 		seqno = (*cond)->c_seqno;
257bb535300SJeff Roberson 
258bb535300SJeff Roberson 		do {
259bb535300SJeff Roberson 			/*
260bb535300SJeff Roberson 			 * Queue the running thread on the condition
261bb535300SJeff Roberson 			 * variable.
262bb535300SJeff Roberson 			 */
263bb535300SJeff Roberson 			cond_queue_enq(*cond, curthread);
264bb535300SJeff Roberson 
265bb535300SJeff Roberson 			if (curthread->cancelflags & PTHREAD_CANCELLING) {
266bb535300SJeff Roberson 				/*
267bb535300SJeff Roberson 				 * POSIX Says that we must relock the mutex
268bb535300SJeff Roberson 				 * even if we're being canceled.
269bb535300SJeff Roberson 				 */
270a224a391SMike Makonnen 				_thread_critical_exit(curthread);
271a224a391SMike Makonnen 				COND_UNLOCK(*cond);
272bb535300SJeff Roberson 				_mutex_cv_lock(mutex);
273bb535300SJeff Roberson 				pthread_testcancel();
274bb535300SJeff Roberson 				PANIC("Shouldn't have come back.");
275bb535300SJeff Roberson 			}
276bb535300SJeff Roberson 
277bb535300SJeff Roberson 			PTHREAD_SET_STATE(curthread, PS_COND_WAIT);
278a224a391SMike Makonnen 			_thread_critical_exit(curthread);
279a224a391SMike Makonnen 			COND_UNLOCK(*cond);
280dd3b229eSMike Makonnen 			rval = _thread_suspend(curthread, (struct timespec *)abstime);
281bb535300SJeff Roberson 			if (rval == -1) {
282bb535300SJeff Roberson 				printf("foo");
283bb535300SJeff Roberson 				fflush(stdout);
284bb535300SJeff Roberson 				abort();
285bb535300SJeff Roberson 			}
286a224a391SMike Makonnen 			COND_LOCK(*cond);
287a224a391SMike Makonnen 			_thread_critical_enter(curthread);
288bb535300SJeff Roberson 
289bb535300SJeff Roberson 			done = (seqno != (*cond)->c_seqno);
290bb535300SJeff Roberson 
291a224a391SMike Makonnen 			/*
292a224a391SMike Makonnen 			 * If we timed out, this will remove us from the
293a224a391SMike Makonnen 			 * queue. Otherwise, if we were signaled it does
294a224a391SMike Makonnen 			 * nothing because this thread won't be on the queue.
295a224a391SMike Makonnen 			 */
296bb535300SJeff Roberson 			cond_queue_remove(*cond, curthread);
297bb535300SJeff Roberson 
298bb535300SJeff Roberson 		} while ((done == 0) && (rval == 0));
299bb535300SJeff Roberson 		/*
300bb535300SJeff Roberson 		 * If we timed out someone still may have signaled us
301bb535300SJeff Roberson 		 * before we got a chance to run again.  We check for
302bb535300SJeff Roberson 		 * this by looking to see if our state is RUNNING.
303bb535300SJeff Roberson 		 */
304bb535300SJeff Roberson 		if (rval == EAGAIN) {
305bb535300SJeff Roberson 			if (curthread->state != PS_RUNNING) {
306bb535300SJeff Roberson 				PTHREAD_SET_STATE(curthread, PS_RUNNING);
307bb535300SJeff Roberson 				rval = ETIMEDOUT;
308bb535300SJeff Roberson 			} else
309bb535300SJeff Roberson 				rval = 0;
310bb535300SJeff Roberson 		}
311a224a391SMike Makonnen 		_thread_critical_exit(curthread);
312a224a391SMike Makonnen 		COND_UNLOCK(*cond);
313bb535300SJeff Roberson 
314bb535300SJeff Roberson 		mtxrval = _mutex_cv_lock(mutex);
315bb535300SJeff Roberson 
316bb535300SJeff Roberson 		/*
317bb535300SJeff Roberson 		 * If the mutex failed return that error, otherwise we're
318bb535300SJeff Roberson 		 * returning ETIMEDOUT.
319bb535300SJeff Roberson 		 */
320bb535300SJeff Roberson 		if (mtxrval == -1) {
321bb535300SJeff Roberson 			printf("foo");
322bb535300SJeff Roberson 			fflush(stdout);
323bb535300SJeff Roberson 			abort();
324bb535300SJeff Roberson 		}
325bb535300SJeff Roberson 		if (mtxrval != 0)
326bb535300SJeff Roberson 			rval = mtxrval;
327bb535300SJeff Roberson 
328bb535300SJeff Roberson 		break;
329bb535300SJeff Roberson 
330bb535300SJeff Roberson 	/* Trap invalid condition variable types: */
331bb535300SJeff Roberson 	default:
332bb535300SJeff Roberson 		COND_UNLOCK(*cond);
333bb535300SJeff Roberson 		rval = EINVAL;
334bb535300SJeff Roberson 		break;
335bb535300SJeff Roberson 	}
336bb535300SJeff Roberson 
337bb535300SJeff Roberson 	_thread_leave_cancellation_point();
338bb535300SJeff Roberson 
339bb535300SJeff Roberson 	return (rval);
340bb535300SJeff Roberson }
341bb535300SJeff Roberson 
342bb535300SJeff Roberson int
343bb535300SJeff Roberson _pthread_cond_signal(pthread_cond_t * cond)
344bb535300SJeff Roberson {
345a224a391SMike Makonnen 	return (cond_signal(cond, 0));
346bb535300SJeff Roberson }
347bb535300SJeff Roberson 
348bb535300SJeff Roberson int
349bb535300SJeff Roberson _pthread_cond_broadcast(pthread_cond_t * cond)
350bb535300SJeff Roberson {
351a224a391SMike Makonnen 	return (cond_signal(cond, 1));
352a224a391SMike Makonnen }
353a224a391SMike Makonnen 
354a224a391SMike Makonnen static int
355a224a391SMike Makonnen cond_signal(pthread_cond_t * cond, int broadcast)
356a224a391SMike Makonnen {
357bb535300SJeff Roberson 	int             rval = 0;
358bb535300SJeff Roberson 	pthread_t       pthread;
359bb535300SJeff Roberson 
360bb535300SJeff Roberson 	if (cond == NULL)
361bb535300SJeff Roberson 		return (EINVAL);
362bb535300SJeff Roberson        /*
363bb535300SJeff Roberson         * If the condition variable is statically initialized, perform dynamic
364bb535300SJeff Roberson         * initialization.
365bb535300SJeff Roberson         */
366bb535300SJeff Roberson 	if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0)
367bb535300SJeff Roberson 		return (rval);
368bb535300SJeff Roberson 
369bb535300SJeff Roberson 	COND_LOCK(*cond);
370bb535300SJeff Roberson 
371bb535300SJeff Roberson 	/* Process according to condition variable type: */
372bb535300SJeff Roberson 	switch ((*cond)->c_type) {
373bb535300SJeff Roberson 	/* Fast condition variable: */
374bb535300SJeff Roberson 	case COND_TYPE_FAST:
375bb535300SJeff Roberson 		(*cond)->c_seqno++;
376bb535300SJeff Roberson 
377bb535300SJeff Roberson 		/*
378a224a391SMike Makonnen 		 * Enter a loop to bring all (or only one) threads off the
379bb535300SJeff Roberson 		 * condition queue:
380bb535300SJeff Roberson 		 */
381a224a391SMike Makonnen 		do {
382bb535300SJeff Roberson 			/*
383a224a391SMike Makonnen 			 * Wake up the signaled thread. It will be returned
384a224a391SMike Makonnen 			 * to us locked, and with signals disabled.
385bb535300SJeff Roberson 			 */
386a224a391SMike Makonnen 			if ((pthread = cond_queue_deq(*cond)) != NULL) {
387bb535300SJeff Roberson 				PTHREAD_NEW_STATE(pthread, PS_RUNNING);
388a224a391SMike Makonnen 				_thread_critical_exit(pthread);
389bb535300SJeff Roberson 			}
390a224a391SMike Makonnen 		} while (broadcast && pthread != NULL);
391bb535300SJeff Roberson 
392bb535300SJeff Roberson 		break;
393bb535300SJeff Roberson 
394bb535300SJeff Roberson 	/* Trap invalid condition variable types: */
395bb535300SJeff Roberson 	default:
396bb535300SJeff Roberson 		rval = EINVAL;
397bb535300SJeff Roberson 		break;
398bb535300SJeff Roberson 	}
399bb535300SJeff Roberson 
400bb535300SJeff Roberson 	COND_UNLOCK(*cond);
401bb535300SJeff Roberson 
402bb535300SJeff Roberson 
403bb535300SJeff Roberson 	return (rval);
404bb535300SJeff Roberson }
405bb535300SJeff Roberson 
406bb535300SJeff Roberson void
407bb535300SJeff Roberson _cond_wait_backout(pthread_t pthread)
408bb535300SJeff Roberson {
409bb535300SJeff Roberson 	pthread_cond_t	cond;
410bb535300SJeff Roberson 
411bb535300SJeff Roberson 	cond = pthread->data.cond;
412bb535300SJeff Roberson 	if (cond == NULL)
413bb535300SJeff Roberson 		return;
414bb535300SJeff Roberson 
415bb535300SJeff Roberson 	COND_LOCK(cond);
416bb535300SJeff Roberson 
417bb535300SJeff Roberson 	/* Process according to condition variable type: */
418bb535300SJeff Roberson 	switch (cond->c_type) {
419bb535300SJeff Roberson 	/* Fast condition variable: */
420bb535300SJeff Roberson 	case COND_TYPE_FAST:
421a224a391SMike Makonnen 		_thread_critical_enter(curthread);
422bb535300SJeff Roberson 
423bb535300SJeff Roberson 		cond_queue_remove(cond, pthread);
424bb535300SJeff Roberson 
425a224a391SMike Makonnen 		_thread_critical_exit(curthread);
426bb535300SJeff Roberson 		break;
427bb535300SJeff Roberson 
428bb535300SJeff Roberson 	default:
429bb535300SJeff Roberson 		break;
430bb535300SJeff Roberson 	}
431bb535300SJeff Roberson 
432bb535300SJeff Roberson 	COND_UNLOCK(cond);
433bb535300SJeff Roberson }
434bb535300SJeff Roberson 
435bb535300SJeff Roberson /*
436bb535300SJeff Roberson  * Dequeue a waiting thread from the head of a condition queue in
437bb535300SJeff Roberson  * descending priority order.
438bb535300SJeff Roberson  */
439bb535300SJeff Roberson static pthread_t
440bb535300SJeff Roberson cond_queue_deq(pthread_cond_t cond)
441bb535300SJeff Roberson {
442bb535300SJeff Roberson 	pthread_t pthread;
443bb535300SJeff Roberson 
444bb535300SJeff Roberson 	while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
445a224a391SMike Makonnen 		_thread_critical_enter(pthread);
446bb535300SJeff Roberson 		TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
447bb535300SJeff Roberson 		cond_queue_remove(cond, pthread);
448bb535300SJeff Roberson 		if ((pthread->cancelflags & PTHREAD_CANCELLING) == 0 &&
449bb535300SJeff Roberson 		    pthread->state == PS_COND_WAIT)
450bb535300SJeff Roberson 			/*
451bb535300SJeff Roberson 			 * Only exit the loop when we find a thread
452bb535300SJeff Roberson 			 * that hasn't timed out or been canceled;
453bb535300SJeff Roberson 			 * those threads are already running and don't
454bb535300SJeff Roberson 			 * need their run state changed.
455bb535300SJeff Roberson 			 */
456bb535300SJeff Roberson 			break;
457a224a391SMike Makonnen 		else
458a224a391SMike Makonnen 			_thread_critical_exit(pthread);
459bb535300SJeff Roberson 	}
460bb535300SJeff Roberson 
461bb535300SJeff Roberson 	return(pthread);
462bb535300SJeff Roberson }
463bb535300SJeff Roberson 
464bb535300SJeff Roberson /*
465bb535300SJeff Roberson  * Remove a waiting thread from a condition queue in descending priority
466bb535300SJeff Roberson  * order.
467bb535300SJeff Roberson  */
468bb535300SJeff Roberson static void
469bb535300SJeff Roberson cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
470bb535300SJeff Roberson {
471bb535300SJeff Roberson 	/*
472bb535300SJeff Roberson 	 * Because pthread_cond_timedwait() can timeout as well
473bb535300SJeff Roberson 	 * as be signaled by another thread, it is necessary to
474bb535300SJeff Roberson 	 * guard against removing the thread from the queue if
475bb535300SJeff Roberson 	 * it isn't in the queue.
476bb535300SJeff Roberson 	 */
477bb535300SJeff Roberson 	if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) {
478bb535300SJeff Roberson 		TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
479bb535300SJeff Roberson 		pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
480bb535300SJeff Roberson 	}
481bb535300SJeff Roberson 	/* Check for no more waiters. */
482bb535300SJeff Roberson 	if (TAILQ_FIRST(&cond->c_queue) == NULL)
483bb535300SJeff Roberson 		cond->c_mutex = NULL;
484bb535300SJeff Roberson }
485bb535300SJeff Roberson 
486bb535300SJeff Roberson /*
487bb535300SJeff Roberson  * Enqueue a waiting thread to a condition queue in descending priority
488bb535300SJeff Roberson  * order.
489bb535300SJeff Roberson  */
490bb535300SJeff Roberson static void
491bb535300SJeff Roberson cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
492bb535300SJeff Roberson {
493bb535300SJeff Roberson 	pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
4946439d4c2SMike Makonnen 	char *name;
495bb535300SJeff Roberson 
4966439d4c2SMike Makonnen 	name = pthread->name ? pthread->name : "unknown";
4976439d4c2SMike Makonnen 	if ((pthread->flags & PTHREAD_FLAGS_IN_CONDQ) != 0)
4986439d4c2SMike Makonnen 		_thread_printf(2, "Thread (%s:%u) already on condq\n",
4996439d4c2SMike Makonnen 		    pthread->name, pthread->uniqueid);
5006439d4c2SMike Makonnen 	if ((pthread->flags & PTHREAD_FLAGS_IN_MUTEXQ) != 0)
5016439d4c2SMike Makonnen 		_thread_printf(2, "Thread (%s:%u) already on mutexq\n",
5026439d4c2SMike Makonnen 		    pthread->name, pthread->uniqueid);
503bb535300SJeff Roberson 	PTHREAD_ASSERT_NOT_IN_SYNCQ(pthread);
504bb535300SJeff Roberson 
505bb535300SJeff Roberson 	/*
506bb535300SJeff Roberson 	 * For the common case of all threads having equal priority,
507bb535300SJeff Roberson 	 * we perform a quick check against the priority of the thread
508bb535300SJeff Roberson 	 * at the tail of the queue.
509bb535300SJeff Roberson 	 */
510bb535300SJeff Roberson 	if ((tid == NULL) || (pthread->active_priority <= tid->active_priority))
511bb535300SJeff Roberson 		TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe);
512bb535300SJeff Roberson 	else {
513bb535300SJeff Roberson 		tid = TAILQ_FIRST(&cond->c_queue);
514bb535300SJeff Roberson 		while (pthread->active_priority <= tid->active_priority)
515bb535300SJeff Roberson 			tid = TAILQ_NEXT(tid, sqe);
516bb535300SJeff Roberson 		TAILQ_INSERT_BEFORE(tid, pthread, sqe);
517bb535300SJeff Roberson 	}
518bb535300SJeff Roberson 	pthread->flags |= PTHREAD_FLAGS_IN_CONDQ;
519bb535300SJeff Roberson 	pthread->data.cond = cond;
520bb535300SJeff Roberson }
521