xref: /freebsd/lib/libthr/thread/thr_cond.c (revision bb535300dd871731b2542594a917bc2892479ca0)
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);
46bb535300SJeff Roberson 
47bb535300SJeff Roberson __weak_reference(_pthread_cond_init, pthread_cond_init);
48bb535300SJeff Roberson __weak_reference(_pthread_cond_destroy, pthread_cond_destroy);
49bb535300SJeff Roberson __weak_reference(_pthread_cond_wait, pthread_cond_wait);
50bb535300SJeff Roberson __weak_reference(_pthread_cond_timedwait, pthread_cond_timedwait);
51bb535300SJeff Roberson __weak_reference(_pthread_cond_signal, pthread_cond_signal);
52bb535300SJeff Roberson __weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
53bb535300SJeff Roberson 
54bb535300SJeff Roberson #define	COND_LOCK(c)						\
55bb535300SJeff Roberson do {								\
56bb535300SJeff Roberson 	if (umtx_lock(&(c)->c_lock, curthread->thr_id))		\
57bb535300SJeff Roberson 		abort();					\
58bb535300SJeff Roberson } while (0)
59bb535300SJeff Roberson 
60bb535300SJeff Roberson #define	COND_UNLOCK(c)						\
61bb535300SJeff Roberson do {								\
62bb535300SJeff Roberson 	if (umtx_unlock(&(c)->c_lock, curthread->thr_id))	\
63bb535300SJeff Roberson 		abort();					\
64bb535300SJeff Roberson } while (0)
65bb535300SJeff Roberson 
66bb535300SJeff Roberson 
67bb535300SJeff Roberson /* Reinitialize a condition variable to defaults. */
68bb535300SJeff Roberson int
69bb535300SJeff Roberson _cond_reinit(pthread_cond_t *cond)
70bb535300SJeff Roberson {
71bb535300SJeff Roberson 	if (cond == NULL)
72bb535300SJeff Roberson 		return (EINVAL);
73bb535300SJeff Roberson 
74bb535300SJeff Roberson 	if (*cond == NULL)
75bb535300SJeff Roberson 		return (pthread_cond_init(cond, NULL));
76bb535300SJeff Roberson 
77bb535300SJeff Roberson 	/*
78bb535300SJeff Roberson 	 * Initialize the condition variable structure:
79bb535300SJeff Roberson 	 */
80bb535300SJeff Roberson 	TAILQ_INIT(&(*cond)->c_queue);
81bb535300SJeff Roberson 	(*cond)->c_flags = COND_FLAGS_INITED;
82bb535300SJeff Roberson 	(*cond)->c_type = COND_TYPE_FAST;
83bb535300SJeff Roberson 	(*cond)->c_mutex = NULL;
84bb535300SJeff Roberson 	(*cond)->c_seqno = 0;
85bb535300SJeff Roberson 	bzero(&(*cond)->c_lock, sizeof((*cond)->c_lock));
86bb535300SJeff Roberson 
87bb535300SJeff Roberson 	return (0);
88bb535300SJeff Roberson }
89bb535300SJeff Roberson 
90bb535300SJeff Roberson int
91bb535300SJeff Roberson _pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
92bb535300SJeff Roberson {
93bb535300SJeff Roberson 	enum pthread_cond_type type;
94bb535300SJeff Roberson 	pthread_cond_t	pcond;
95bb535300SJeff Roberson 
96bb535300SJeff Roberson 	if (cond == NULL)
97bb535300SJeff Roberson 		return (EINVAL);
98bb535300SJeff Roberson 
99bb535300SJeff Roberson 	/*
100bb535300SJeff Roberson 	 * Check if a pointer to a condition variable attribute
101bb535300SJeff Roberson 	 * structure was passed by the caller:
102bb535300SJeff Roberson 	 */
103bb535300SJeff Roberson 	if (cond_attr != NULL && *cond_attr != NULL)
104bb535300SJeff Roberson 		type = (*cond_attr)->c_type;
105bb535300SJeff Roberson 	else
106bb535300SJeff Roberson 		/* Default to a fast condition variable: */
107bb535300SJeff Roberson 		type = COND_TYPE_FAST;
108bb535300SJeff Roberson 
109bb535300SJeff Roberson 	/* Process according to condition variable type: */
110bb535300SJeff Roberson 	switch (type) {
111bb535300SJeff Roberson 	case COND_TYPE_FAST:
112bb535300SJeff Roberson 		break;
113bb535300SJeff Roberson 	default:
114bb535300SJeff Roberson 		return (EINVAL);
115bb535300SJeff Roberson 		break;
116bb535300SJeff Roberson 	}
117bb535300SJeff Roberson 
118bb535300SJeff Roberson 	if ((pcond = (pthread_cond_t)
119bb535300SJeff Roberson 	    malloc(sizeof(struct pthread_cond))) == NULL)
120bb535300SJeff Roberson 		return (ENOMEM);
121bb535300SJeff Roberson 	/*
122bb535300SJeff Roberson 	 * Initialise the condition variable
123bb535300SJeff Roberson 	 * structure:
124bb535300SJeff Roberson 	 */
125bb535300SJeff Roberson 	TAILQ_INIT(&pcond->c_queue);
126bb535300SJeff Roberson 	pcond->c_flags |= COND_FLAGS_INITED;
127bb535300SJeff Roberson 	pcond->c_type = type;
128bb535300SJeff Roberson 	pcond->c_mutex = NULL;
129bb535300SJeff Roberson 	pcond->c_seqno = 0;
130bb535300SJeff Roberson 	bzero(&pcond->c_lock, sizeof(pcond->c_lock));
131bb535300SJeff Roberson 
132bb535300SJeff Roberson 	*cond = pcond;
133bb535300SJeff Roberson 
134bb535300SJeff Roberson 	return (0);
135bb535300SJeff Roberson }
136bb535300SJeff Roberson 
137bb535300SJeff Roberson int
138bb535300SJeff Roberson _pthread_cond_destroy(pthread_cond_t *cond)
139bb535300SJeff Roberson {
140bb535300SJeff Roberson 	struct pthread	*curthread = _get_curthread();
141bb535300SJeff Roberson 
142bb535300SJeff Roberson 	if (cond == NULL || *cond == NULL)
143bb535300SJeff Roberson 		return (EINVAL);
144bb535300SJeff Roberson 
145bb535300SJeff Roberson 	COND_LOCK(*cond);
146bb535300SJeff Roberson 
147bb535300SJeff Roberson 	/*
148bb535300SJeff Roberson 	 * Free the memory allocated for the condition
149bb535300SJeff Roberson 	 * variable structure:
150bb535300SJeff Roberson 	 */
151bb535300SJeff Roberson 	free(*cond);
152bb535300SJeff Roberson 
153bb535300SJeff Roberson 	/*
154bb535300SJeff Roberson 	 * NULL the caller's pointer now that the condition
155bb535300SJeff Roberson 	 * variable has been destroyed:
156bb535300SJeff Roberson 	 */
157bb535300SJeff Roberson 	*cond = NULL;
158bb535300SJeff Roberson 
159bb535300SJeff Roberson 	return (0);
160bb535300SJeff Roberson }
161bb535300SJeff Roberson 
162bb535300SJeff Roberson int
163bb535300SJeff Roberson _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
164bb535300SJeff Roberson {
165bb535300SJeff Roberson 	int rval;
166bb535300SJeff Roberson 	struct timespec abstime = { 0, 0 };
167bb535300SJeff Roberson 
168bb535300SJeff Roberson 	/*
169bb535300SJeff Roberson 	 * XXXTHR This is a hack.  Make a pthread_cond_common function that
170bb535300SJeff Roberson 	 * accepts NULL so we don't change posix semantics for timedwait.
171bb535300SJeff Roberson 	 */
172bb535300SJeff Roberson 	rval = pthread_cond_timedwait(cond, mutex, &abstime);
173bb535300SJeff Roberson 
174bb535300SJeff Roberson 	/* This should never happen. */
175bb535300SJeff Roberson 	if (rval == ETIMEDOUT)
176bb535300SJeff Roberson 		abort();
177bb535300SJeff Roberson 
178bb535300SJeff Roberson 	return (rval);
179bb535300SJeff Roberson }
180bb535300SJeff Roberson 
181bb535300SJeff Roberson int
182bb535300SJeff Roberson _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
183bb535300SJeff Roberson 		       const struct timespec * abstime)
184bb535300SJeff Roberson {
185bb535300SJeff Roberson 	struct pthread	*curthread = _get_curthread();
186bb535300SJeff Roberson 	struct timespec *time;
187bb535300SJeff Roberson 	int	rval = 0;
188bb535300SJeff Roberson 	int	done = 0;
189bb535300SJeff Roberson 	int	seqno;
190bb535300SJeff Roberson 	int	mtxrval;
191bb535300SJeff Roberson 
192bb535300SJeff Roberson 
193bb535300SJeff Roberson 	_thread_enter_cancellation_point();
194bb535300SJeff Roberson 
195bb535300SJeff Roberson 	if (abstime == NULL || abstime->tv_nsec >= 1000000000)
196bb535300SJeff Roberson 		return (EINVAL);
197bb535300SJeff Roberson 
198bb535300SJeff Roberson 	if (abstime->tv_sec == 0 && abstime->tv_nsec == 0)
199bb535300SJeff Roberson 		time = NULL;
200bb535300SJeff Roberson 	else
201bb535300SJeff Roberson 		time = abstime;
202bb535300SJeff Roberson 	/*
203bb535300SJeff Roberson 	 * If the condition variable is statically initialized, perform dynamic
204bb535300SJeff Roberson 	 * initialization.
205bb535300SJeff Roberson 	 */
206bb535300SJeff Roberson 	if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0)
207bb535300SJeff Roberson 		return (rval);
208bb535300SJeff Roberson 
209bb535300SJeff Roberson 
210bb535300SJeff Roberson 	COND_LOCK(*cond);
211bb535300SJeff Roberson 
212bb535300SJeff Roberson 	/*
213bb535300SJeff Roberson 	 * If the condvar was statically allocated, properly
214bb535300SJeff Roberson 	 * initialize the tail queue.
215bb535300SJeff Roberson 	 */
216bb535300SJeff Roberson 	if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
217bb535300SJeff Roberson 		TAILQ_INIT(&(*cond)->c_queue);
218bb535300SJeff Roberson 		(*cond)->c_flags |= COND_FLAGS_INITED;
219bb535300SJeff Roberson 	}
220bb535300SJeff Roberson 
221bb535300SJeff Roberson 	/* Process according to condition variable type. */
222bb535300SJeff Roberson 
223bb535300SJeff Roberson 	switch ((*cond)->c_type) {
224bb535300SJeff Roberson 	/* Fast condition variable: */
225bb535300SJeff Roberson 	case COND_TYPE_FAST:
226bb535300SJeff Roberson 		if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
227bb535300SJeff Roberson 		    ((*cond)->c_mutex != *mutex))) {
228bb535300SJeff Roberson 			COND_UNLOCK(*cond);
229bb535300SJeff Roberson 			rval = EINVAL;
230bb535300SJeff Roberson 			break;
231bb535300SJeff Roberson 		}
232bb535300SJeff Roberson 		/* Remember the mutex */
233bb535300SJeff Roberson 		(*cond)->c_mutex = *mutex;
234bb535300SJeff Roberson 
235bb535300SJeff Roberson 		if ((rval = _mutex_cv_unlock(mutex)) != 0) {
236bb535300SJeff Roberson 			if (rval == -1){
237bb535300SJeff Roberson 				printf("foo");
238bb535300SJeff Roberson 				fflush(stdout);
239bb535300SJeff Roberson 				abort();
240bb535300SJeff Roberson 			}
241bb535300SJeff Roberson 
242bb535300SJeff Roberson 			COND_UNLOCK(*cond);
243bb535300SJeff Roberson 			break;
244bb535300SJeff Roberson 		}
245bb535300SJeff Roberson 		COND_UNLOCK(*cond);
246bb535300SJeff Roberson 
247bb535300SJeff Roberson 		/*
248bb535300SJeff Roberson 		 * We need giant for the queue operations.  It also
249bb535300SJeff Roberson 		 * protects seqno and the pthread flag fields.  This is
250bb535300SJeff Roberson 		 * dropped and reacquired in _thread_suspend().
251bb535300SJeff Roberson 		 */
252bb535300SJeff Roberson 
253bb535300SJeff Roberson 		GIANT_LOCK(curthread);
254bb535300SJeff Roberson 		/*
255bb535300SJeff Roberson 		 * c_seqno is protected by giant.
256bb535300SJeff Roberson 		 */
257bb535300SJeff Roberson 		seqno = (*cond)->c_seqno;
258bb535300SJeff Roberson 
259bb535300SJeff Roberson 		do {
260bb535300SJeff Roberson 			/*
261bb535300SJeff Roberson 			 * Queue the running thread on the condition
262bb535300SJeff Roberson 			 * variable.
263bb535300SJeff Roberson 			 */
264bb535300SJeff Roberson 			cond_queue_enq(*cond, curthread);
265bb535300SJeff Roberson 
266bb535300SJeff Roberson 			if (curthread->cancelflags & PTHREAD_CANCELLING) {
267bb535300SJeff Roberson 				/*
268bb535300SJeff Roberson 				 * POSIX Says that we must relock the mutex
269bb535300SJeff Roberson 				 * even if we're being canceled.
270bb535300SJeff Roberson 				 */
271bb535300SJeff Roberson 				GIANT_UNLOCK(curthread);
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);
278bb535300SJeff Roberson 			rval = _thread_suspend(curthread, time);
279bb535300SJeff Roberson 			if (rval == -1) {
280bb535300SJeff Roberson 				printf("foo");
281bb535300SJeff Roberson 				fflush(stdout);
282bb535300SJeff Roberson 				abort();
283bb535300SJeff Roberson 			}
284bb535300SJeff Roberson 
285bb535300SJeff Roberson 			done = (seqno != (*cond)->c_seqno);
286bb535300SJeff Roberson 
287bb535300SJeff Roberson 			cond_queue_remove(*cond, curthread);
288bb535300SJeff Roberson 
289bb535300SJeff Roberson 		} while ((done == 0) && (rval == 0));
290bb535300SJeff Roberson 		/*
291bb535300SJeff Roberson 		 * If we timed out someone still may have signaled us
292bb535300SJeff Roberson 		 * before we got a chance to run again.  We check for
293bb535300SJeff Roberson 		 * this by looking to see if our state is RUNNING.
294bb535300SJeff Roberson 		 */
295bb535300SJeff Roberson 		if (rval == EAGAIN) {
296bb535300SJeff Roberson 			if (curthread->state != PS_RUNNING) {
297bb535300SJeff Roberson 				PTHREAD_SET_STATE(curthread, PS_RUNNING);
298bb535300SJeff Roberson 				rval = ETIMEDOUT;
299bb535300SJeff Roberson 			} else
300bb535300SJeff Roberson 				rval = 0;
301bb535300SJeff Roberson 		}
302bb535300SJeff Roberson 		GIANT_UNLOCK(curthread);
303bb535300SJeff Roberson 
304bb535300SJeff Roberson 		mtxrval = _mutex_cv_lock(mutex);
305bb535300SJeff Roberson 
306bb535300SJeff Roberson 		/*
307bb535300SJeff Roberson 		 * If the mutex failed return that error, otherwise we're
308bb535300SJeff Roberson 		 * returning ETIMEDOUT.
309bb535300SJeff Roberson 		 */
310bb535300SJeff Roberson 		if (mtxrval == -1) {
311bb535300SJeff Roberson 			printf("foo");
312bb535300SJeff Roberson 			fflush(stdout);
313bb535300SJeff Roberson 			abort();
314bb535300SJeff Roberson 		}
315bb535300SJeff Roberson 		if (mtxrval != 0)
316bb535300SJeff Roberson 			rval = mtxrval;
317bb535300SJeff Roberson 
318bb535300SJeff Roberson 		break;
319bb535300SJeff Roberson 
320bb535300SJeff Roberson 	/* Trap invalid condition variable types: */
321bb535300SJeff Roberson 	default:
322bb535300SJeff Roberson 		COND_UNLOCK(*cond);
323bb535300SJeff Roberson 		rval = EINVAL;
324bb535300SJeff Roberson 		break;
325bb535300SJeff Roberson 	}
326bb535300SJeff Roberson 
327bb535300SJeff Roberson 	/*
328bb535300SJeff Roberson 	 * See if we have to cancel before we retry.  We could be
329bb535300SJeff Roberson 	 * canceled with the mutex held here!
330bb535300SJeff Roberson 	 */
331bb535300SJeff Roberson 	pthread_testcancel();
332bb535300SJeff Roberson 
333bb535300SJeff Roberson 	_thread_leave_cancellation_point();
334bb535300SJeff Roberson 
335bb535300SJeff Roberson 	return (rval);
336bb535300SJeff Roberson }
337bb535300SJeff Roberson 
338bb535300SJeff Roberson int
339bb535300SJeff Roberson _pthread_cond_signal(pthread_cond_t * cond)
340bb535300SJeff Roberson {
341bb535300SJeff Roberson 	struct pthread	*curthread = _get_curthread();
342bb535300SJeff Roberson 	int             rval = 0;
343bb535300SJeff Roberson 	pthread_t       pthread;
344bb535300SJeff Roberson 
345bb535300SJeff Roberson 	if (cond == NULL)
346bb535300SJeff Roberson 		return (EINVAL);
347bb535300SJeff Roberson        /*
348bb535300SJeff Roberson         * If the condition variable is statically initialized, perform dynamic
349bb535300SJeff Roberson         * initialization.
350bb535300SJeff Roberson         */
351bb535300SJeff Roberson 	if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0)
352bb535300SJeff Roberson 		return (rval);
353bb535300SJeff Roberson 
354bb535300SJeff Roberson 
355bb535300SJeff Roberson 	COND_LOCK(*cond);
356bb535300SJeff Roberson 
357bb535300SJeff Roberson 	/* Process according to condition variable type: */
358bb535300SJeff Roberson 	switch ((*cond)->c_type) {
359bb535300SJeff Roberson 	/* Fast condition variable: */
360bb535300SJeff Roberson 	case COND_TYPE_FAST:
361bb535300SJeff Roberson 		GIANT_LOCK(curthread);
362bb535300SJeff Roberson 		(*cond)->c_seqno++;
363bb535300SJeff Roberson 
364bb535300SJeff Roberson 		if ((pthread = cond_queue_deq(*cond)) != NULL) {
365bb535300SJeff Roberson 			/*
366bb535300SJeff Roberson 			 * Wake up the signaled thread:
367bb535300SJeff Roberson 			 */
368bb535300SJeff Roberson 			PTHREAD_NEW_STATE(pthread, PS_RUNNING);
369bb535300SJeff Roberson 		}
370bb535300SJeff Roberson 
371bb535300SJeff Roberson 		GIANT_UNLOCK(curthread);
372bb535300SJeff Roberson 		break;
373bb535300SJeff Roberson 
374bb535300SJeff Roberson 	/* Trap invalid condition variable types: */
375bb535300SJeff Roberson 	default:
376bb535300SJeff Roberson 		rval = EINVAL;
377bb535300SJeff Roberson 		break;
378bb535300SJeff Roberson 	}
379bb535300SJeff Roberson 
380bb535300SJeff Roberson 
381bb535300SJeff Roberson 	COND_UNLOCK(*cond);
382bb535300SJeff Roberson 
383bb535300SJeff Roberson 	return (rval);
384bb535300SJeff Roberson }
385bb535300SJeff Roberson 
386bb535300SJeff Roberson int
387bb535300SJeff Roberson _pthread_cond_broadcast(pthread_cond_t * cond)
388bb535300SJeff Roberson {
389bb535300SJeff Roberson 	struct pthread	*curthread = _get_curthread();
390bb535300SJeff Roberson 	int             rval = 0;
391bb535300SJeff Roberson 	pthread_t       pthread;
392bb535300SJeff Roberson 
393bb535300SJeff Roberson 	if (cond == NULL)
394bb535300SJeff Roberson 		return (EINVAL);
395bb535300SJeff Roberson        /*
396bb535300SJeff Roberson         * If the condition variable is statically initialized, perform dynamic
397bb535300SJeff Roberson         * initialization.
398bb535300SJeff Roberson         */
399bb535300SJeff Roberson 	if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0)
400bb535300SJeff Roberson 		return (rval);
401bb535300SJeff Roberson 
402bb535300SJeff Roberson 	COND_LOCK(*cond);
403bb535300SJeff Roberson 
404bb535300SJeff Roberson 	/* Process according to condition variable type: */
405bb535300SJeff Roberson 	switch ((*cond)->c_type) {
406bb535300SJeff Roberson 	/* Fast condition variable: */
407bb535300SJeff Roberson 	case COND_TYPE_FAST:
408bb535300SJeff Roberson 		GIANT_LOCK(curthread);
409bb535300SJeff Roberson 		(*cond)->c_seqno++;
410bb535300SJeff Roberson 
411bb535300SJeff Roberson 		/*
412bb535300SJeff Roberson 		 * Enter a loop to bring all threads off the
413bb535300SJeff Roberson 		 * condition queue:
414bb535300SJeff Roberson 		 */
415bb535300SJeff Roberson 		while ((pthread = cond_queue_deq(*cond)) != NULL) {
416bb535300SJeff Roberson 			/*
417bb535300SJeff Roberson 			 * Wake up the signaled thread:
418bb535300SJeff Roberson 			 */
419bb535300SJeff Roberson 			PTHREAD_NEW_STATE(pthread, PS_RUNNING);
420bb535300SJeff Roberson 		}
421bb535300SJeff Roberson 		GIANT_UNLOCK(curthread);
422bb535300SJeff Roberson 
423bb535300SJeff Roberson 		/* There are no more waiting threads: */
424bb535300SJeff Roberson 		(*cond)->c_mutex = NULL;
425bb535300SJeff Roberson 
426bb535300SJeff Roberson 		break;
427bb535300SJeff Roberson 
428bb535300SJeff Roberson 	/* Trap invalid condition variable types: */
429bb535300SJeff Roberson 	default:
430bb535300SJeff Roberson 		rval = EINVAL;
431bb535300SJeff Roberson 		break;
432bb535300SJeff Roberson 	}
433bb535300SJeff Roberson 
434bb535300SJeff Roberson 	COND_UNLOCK(*cond);
435bb535300SJeff Roberson 
436bb535300SJeff Roberson 
437bb535300SJeff Roberson 	return (rval);
438bb535300SJeff Roberson }
439bb535300SJeff Roberson 
440bb535300SJeff Roberson void
441bb535300SJeff Roberson _cond_wait_backout(pthread_t pthread)
442bb535300SJeff Roberson {
443bb535300SJeff Roberson 	struct pthread	*curthread = _get_curthread();
444bb535300SJeff Roberson 	pthread_cond_t	cond;
445bb535300SJeff Roberson 
446bb535300SJeff Roberson 	cond = pthread->data.cond;
447bb535300SJeff Roberson 	if (cond == NULL)
448bb535300SJeff Roberson 		return;
449bb535300SJeff Roberson 
450bb535300SJeff Roberson 	COND_LOCK(cond);
451bb535300SJeff Roberson 
452bb535300SJeff Roberson 	/* Process according to condition variable type: */
453bb535300SJeff Roberson 	switch (cond->c_type) {
454bb535300SJeff Roberson 	/* Fast condition variable: */
455bb535300SJeff Roberson 	case COND_TYPE_FAST:
456bb535300SJeff Roberson 		GIANT_LOCK(curthread);
457bb535300SJeff Roberson 
458bb535300SJeff Roberson 		cond_queue_remove(cond, pthread);
459bb535300SJeff Roberson 
460bb535300SJeff Roberson 		GIANT_UNLOCK(curthread);
461bb535300SJeff Roberson 		break;
462bb535300SJeff Roberson 
463bb535300SJeff Roberson 	default:
464bb535300SJeff Roberson 		break;
465bb535300SJeff Roberson 	}
466bb535300SJeff Roberson 
467bb535300SJeff Roberson 	COND_UNLOCK(cond);
468bb535300SJeff Roberson }
469bb535300SJeff Roberson 
470bb535300SJeff Roberson /*
471bb535300SJeff Roberson  * Dequeue a waiting thread from the head of a condition queue in
472bb535300SJeff Roberson  * descending priority order.
473bb535300SJeff Roberson  */
474bb535300SJeff Roberson static pthread_t
475bb535300SJeff Roberson cond_queue_deq(pthread_cond_t cond)
476bb535300SJeff Roberson {
477bb535300SJeff Roberson 	pthread_t pthread;
478bb535300SJeff Roberson 
479bb535300SJeff Roberson 	while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
480bb535300SJeff Roberson 		TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
481bb535300SJeff Roberson 		cond_queue_remove(cond, pthread);
482bb535300SJeff Roberson 		if ((pthread->cancelflags & PTHREAD_CANCELLING) == 0 &&
483bb535300SJeff Roberson 		    pthread->state == PS_COND_WAIT)
484bb535300SJeff Roberson 			/*
485bb535300SJeff Roberson 			 * Only exit the loop when we find a thread
486bb535300SJeff Roberson 			 * that hasn't timed out or been canceled;
487bb535300SJeff Roberson 			 * those threads are already running and don't
488bb535300SJeff Roberson 			 * need their run state changed.
489bb535300SJeff Roberson 			 */
490bb535300SJeff Roberson 			break;
491bb535300SJeff Roberson 	}
492bb535300SJeff Roberson 
493bb535300SJeff Roberson 	return(pthread);
494bb535300SJeff Roberson }
495bb535300SJeff Roberson 
496bb535300SJeff Roberson /*
497bb535300SJeff Roberson  * Remove a waiting thread from a condition queue in descending priority
498bb535300SJeff Roberson  * order.
499bb535300SJeff Roberson  */
500bb535300SJeff Roberson static void
501bb535300SJeff Roberson cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
502bb535300SJeff Roberson {
503bb535300SJeff Roberson 	/*
504bb535300SJeff Roberson 	 * Because pthread_cond_timedwait() can timeout as well
505bb535300SJeff Roberson 	 * as be signaled by another thread, it is necessary to
506bb535300SJeff Roberson 	 * guard against removing the thread from the queue if
507bb535300SJeff Roberson 	 * it isn't in the queue.
508bb535300SJeff Roberson 	 */
509bb535300SJeff Roberson 	if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) {
510bb535300SJeff Roberson 		TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
511bb535300SJeff Roberson 		pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
512bb535300SJeff Roberson 	}
513bb535300SJeff Roberson 	/* Check for no more waiters. */
514bb535300SJeff Roberson 	if (TAILQ_FIRST(&cond->c_queue) == NULL)
515bb535300SJeff Roberson 		cond->c_mutex = NULL;
516bb535300SJeff Roberson }
517bb535300SJeff Roberson 
518bb535300SJeff Roberson /*
519bb535300SJeff Roberson  * Enqueue a waiting thread to a condition queue in descending priority
520bb535300SJeff Roberson  * order.
521bb535300SJeff Roberson  */
522bb535300SJeff Roberson static void
523bb535300SJeff Roberson cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
524bb535300SJeff Roberson {
525bb535300SJeff Roberson 	pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
526bb535300SJeff Roberson 
527bb535300SJeff Roberson 	PTHREAD_ASSERT_NOT_IN_SYNCQ(pthread);
528bb535300SJeff Roberson 
529bb535300SJeff Roberson 	/*
530bb535300SJeff Roberson 	 * For the common case of all threads having equal priority,
531bb535300SJeff Roberson 	 * we perform a quick check against the priority of the thread
532bb535300SJeff Roberson 	 * at the tail of the queue.
533bb535300SJeff Roberson 	 */
534bb535300SJeff Roberson 	if ((tid == NULL) || (pthread->active_priority <= tid->active_priority))
535bb535300SJeff Roberson 		TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe);
536bb535300SJeff Roberson 	else {
537bb535300SJeff Roberson 		tid = TAILQ_FIRST(&cond->c_queue);
538bb535300SJeff Roberson 		while (pthread->active_priority <= tid->active_priority)
539bb535300SJeff Roberson 			tid = TAILQ_NEXT(tid, sqe);
540bb535300SJeff Roberson 		TAILQ_INSERT_BEFORE(tid, pthread, sqe);
541bb535300SJeff Roberson 	}
542bb535300SJeff Roberson 	pthread->flags |= PTHREAD_FLAGS_IN_CONDQ;
543bb535300SJeff Roberson 	pthread->data.cond = cond;
544bb535300SJeff Roberson }
545