thr_cond.c revision 50601
10Sstevel@tonic-gate/*
20Sstevel@tonic-gate * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
30Sstevel@tonic-gate * All rights reserved.
40Sstevel@tonic-gate *
5420Sstevel * Redistribution and use in source and binary forms, with or without
6420Sstevel * modification, are permitted provided that the following conditions
70Sstevel@tonic-gate * are met:
80Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright
90Sstevel@tonic-gate *    notice, this list of conditions and the following disclaimer.
100Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright
110Sstevel@tonic-gate *    notice, this list of conditions and the following disclaimer in the
120Sstevel@tonic-gate *    documentation and/or other materials provided with the distribution.
130Sstevel@tonic-gate * 3. All advertising materials mentioning features or use of this software
140Sstevel@tonic-gate *    must display the following acknowledgement:
150Sstevel@tonic-gate *	This product includes software developed by John Birrell.
160Sstevel@tonic-gate * 4. Neither the name of the author nor the names of any co-contributors
170Sstevel@tonic-gate *    may be used to endorse or promote products derived from this software
180Sstevel@tonic-gate *    without specific prior written permission.
190Sstevel@tonic-gate *
2059Sjbeck * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
2159Sjbeck * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2259Sjbeck * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2312041SJohn.Beck@Sun.COM * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
240Sstevel@tonic-gate * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
250Sstevel@tonic-gate * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
260Sstevel@tonic-gate * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
270Sstevel@tonic-gate * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
280Sstevel@tonic-gate * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
290Sstevel@tonic-gate * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
300Sstevel@tonic-gate * SUCH DAMAGE.
310Sstevel@tonic-gate *
320Sstevel@tonic-gate * $FreeBSD: head/lib/libkse/thread/thr_cond.c 50601 1999-08-30 00:02:08Z deischen $
330Sstevel@tonic-gate */
340Sstevel@tonic-gate#include <stdlib.h>
350Sstevel@tonic-gate#include <errno.h>
360Sstevel@tonic-gate#include <string.h>
370Sstevel@tonic-gate#ifdef _THREAD_SAFE
380Sstevel@tonic-gate#include <pthread.h>
390Sstevel@tonic-gate#include "pthread_private.h"
400Sstevel@tonic-gate
410Sstevel@tonic-gate/*
420Sstevel@tonic-gate * Prototypes
430Sstevel@tonic-gate */
440Sstevel@tonic-gatestatic inline pthread_t	cond_queue_deq(pthread_cond_t);
450Sstevel@tonic-gatestatic inline void	cond_queue_remove(pthread_cond_t, pthread_t);
460Sstevel@tonic-gatestatic inline void	cond_queue_enq(pthread_cond_t, pthread_t);
470Sstevel@tonic-gate
48/* Reinitialize a condition variable to defaults. */
49int
50_cond_reinit(pthread_cond_t * cond)
51{
52	int ret = 0;
53
54	if (cond == NULL)
55		ret = EINVAL;
56	else if (*cond == NULL)
57		ret = pthread_cond_init(cond, NULL);
58	else {
59		/*
60		 * Initialize the condition variable structure:
61		 */
62		TAILQ_INIT(&(*cond)->c_queue);
63		(*cond)->c_flags = COND_FLAGS_INITED;
64		(*cond)->c_type = COND_TYPE_FAST;
65		(*cond)->c_mutex = NULL;
66		memset(&(*cond)->lock, 0, sizeof((*cond)->lock));
67	}
68	return (ret);
69}
70
71int
72pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
73{
74	enum pthread_cond_type type;
75	pthread_cond_t	pcond;
76	int             rval = 0;
77
78	if (cond == NULL)
79		rval = EINVAL;
80	else {
81		/*
82		 * Check if a pointer to a condition variable attribute
83		 * structure was passed by the caller:
84		 */
85		if (cond_attr != NULL && *cond_attr != NULL) {
86			/* Default to a fast condition variable: */
87			type = (*cond_attr)->c_type;
88		} else {
89			/* Default to a fast condition variable: */
90			type = COND_TYPE_FAST;
91		}
92
93		/* Process according to condition variable type: */
94		switch (type) {
95		/* Fast condition variable: */
96		case COND_TYPE_FAST:
97			/* Nothing to do here. */
98			break;
99
100		/* Trap invalid condition variable types: */
101		default:
102			/* Return an invalid argument error: */
103			rval = EINVAL;
104			break;
105		}
106
107		/* Check for no errors: */
108		if (rval == 0) {
109			if ((pcond = (pthread_cond_t)
110			    malloc(sizeof(struct pthread_cond))) == NULL) {
111				rval = ENOMEM;
112			} else {
113				/*
114				 * Initialise the condition variable
115				 * structure:
116				 */
117				TAILQ_INIT(&pcond->c_queue);
118				pcond->c_flags |= COND_FLAGS_INITED;
119				pcond->c_type = type;
120				pcond->c_mutex = NULL;
121				memset(&pcond->lock,0,sizeof(pcond->lock));
122				*cond = pcond;
123			}
124		}
125	}
126	/* Return the completion status: */
127	return (rval);
128}
129
130int
131pthread_cond_destroy(pthread_cond_t * cond)
132{
133	int             rval = 0;
134
135	if (cond == NULL || *cond == NULL)
136		rval = EINVAL;
137	else {
138		/* Lock the condition variable structure: */
139		_SPINLOCK(&(*cond)->lock);
140
141		/*
142		 * Free the memory allocated for the condition
143		 * variable structure:
144		 */
145		free(*cond);
146
147		/*
148		 * NULL the caller's pointer now that the condition
149		 * variable has been destroyed:
150		 */
151		*cond = NULL;
152	}
153	/* Return the completion status: */
154	return (rval);
155}
156
157int
158pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
159{
160	int             rval = 0;
161	int             status;
162
163	if (cond == NULL)
164		rval = EINVAL;
165
166	/*
167	 * If the condition variable is statically initialized,
168	 * perform the dynamic initialization:
169	 */
170	else if (*cond != NULL ||
171	    (rval = pthread_cond_init(cond,NULL)) == 0) {
172		/* Lock the condition variable structure: */
173		_SPINLOCK(&(*cond)->lock);
174
175		/*
176		 * If the condvar was statically allocated, properly
177		 * initialize the tail queue.
178		 */
179		if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
180			TAILQ_INIT(&(*cond)->c_queue);
181			(*cond)->c_flags |= COND_FLAGS_INITED;
182		}
183
184		/* Process according to condition variable type: */
185		switch ((*cond)->c_type) {
186		/* Fast condition variable: */
187		case COND_TYPE_FAST:
188			if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
189			    ((*cond)->c_mutex != *mutex))) {
190				/* Unlock the condition variable structure: */
191				_SPINUNLOCK(&(*cond)->lock);
192
193				/* Return invalid argument error: */
194				rval = EINVAL;
195			} else {
196				/* Reset the timeout flag: */
197				_thread_run->timeout = 0;
198
199				/*
200				 * Queue the running thread for the condition
201				 * variable:
202				 */
203				cond_queue_enq(*cond, _thread_run);
204
205				/* Remember the mutex that is being used: */
206				(*cond)->c_mutex = *mutex;
207
208				/* Wait forever: */
209				_thread_run->wakeup_time.tv_sec = -1;
210
211				/* Unlock the mutex: */
212				if ((rval = _mutex_cv_unlock(mutex)) != 0) {
213					/*
214					 * Cannot unlock the mutex, so remove
215					 * the running thread from the condition
216					 * variable queue:
217					 */
218					cond_queue_remove(*cond, _thread_run);
219
220					/* Check for no more waiters: */
221					if (TAILQ_FIRST(&(*cond)->c_queue) ==
222					    NULL)
223						(*cond)->c_mutex = NULL;
224
225					/* Unlock the condition variable structure: */
226					_SPINUNLOCK(&(*cond)->lock);
227				}
228				else {
229					/*
230					 * Schedule the next thread and unlock
231					 * the condition variable structure:
232					 */
233					_thread_kern_sched_state_unlock(PS_COND_WAIT,
234				    	    &(*cond)->lock, __FILE__, __LINE__);
235
236					/* Lock the mutex: */
237					rval = _mutex_cv_lock(mutex);
238				}
239			}
240			break;
241
242		/* Trap invalid condition variable types: */
243		default:
244			/* Unlock the condition variable structure: */
245			_SPINUNLOCK(&(*cond)->lock);
246
247			/* Return an invalid argument error: */
248			rval = EINVAL;
249			break;
250		}
251	}
252
253	/* Return the completion status: */
254	return (rval);
255}
256
257int
258pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
259		       const struct timespec * abstime)
260{
261	int             rval = 0;
262	int             status;
263
264	if (cond == NULL || abstime == NULL)
265		rval = EINVAL;
266
267	if (abstime->tv_sec < 0 ||
268		abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
269		errno = EINVAL;
270		return (-1);
271	}
272
273	/*
274	 * If the condition variable is statically initialized,
275	 * perform the dynamic initialization:
276	 */
277	if (*cond != NULL ||
278	    (rval = pthread_cond_init(cond,NULL)) == 0) {
279		/* Lock the condition variable structure: */
280		_SPINLOCK(&(*cond)->lock);
281
282		/*
283		 * If the condvar was statically allocated, properly
284		 * initialize the tail queue.
285		 */
286		if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
287			TAILQ_INIT(&(*cond)->c_queue);
288			(*cond)->c_flags |= COND_FLAGS_INITED;
289		}
290
291		/* Process according to condition variable type: */
292		switch ((*cond)->c_type) {
293		/* Fast condition variable: */
294		case COND_TYPE_FAST:
295			if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
296			    ((*cond)->c_mutex != *mutex))) {
297				/* Return invalid argument error: */
298				rval = EINVAL;
299
300				/* Unlock the condition variable structure: */
301				_SPINUNLOCK(&(*cond)->lock);
302			} else {
303				/* Set the wakeup time: */
304				_thread_run->wakeup_time.tv_sec =
305				    abstime->tv_sec;
306				_thread_run->wakeup_time.tv_nsec =
307				    abstime->tv_nsec;
308
309				/* Reset the timeout flag: */
310				_thread_run->timeout = 0;
311
312				/*
313				 * Queue the running thread for the condition
314				 * variable:
315				 */
316				cond_queue_enq(*cond, _thread_run);
317
318				/* Remember the mutex that is being used: */
319				(*cond)->c_mutex = *mutex;
320
321				/* Unlock the mutex: */
322				if ((rval = _mutex_cv_unlock(mutex)) != 0) {
323					/*
324					 * Cannot unlock the mutex, so remove
325					 * the running thread from the condition
326					 * variable queue:
327					 */
328					cond_queue_remove(*cond, _thread_run);
329
330					/* Check for no more waiters: */
331					if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
332						(*cond)->c_mutex = NULL;
333
334					/* Unlock the condition variable structure: */
335					_SPINUNLOCK(&(*cond)->lock);
336				} else {
337					/*
338					 * Schedule the next thread and unlock
339					 * the condition variable structure:
340					 */
341					_thread_kern_sched_state_unlock(PS_COND_WAIT,
342				  	     &(*cond)->lock, __FILE__, __LINE__);
343
344					/* Check if the wait timedout: */
345					if (_thread_run->timeout == 0) {
346						/* Lock the mutex: */
347						rval = _mutex_cv_lock(mutex);
348					}
349					else {
350						/* Lock the condition variable structure: */
351						_SPINLOCK(&(*cond)->lock);
352
353						/*
354						 * The wait timed out; remove
355						 * the thread from the condition
356					 	 * variable queue:
357						 */
358						cond_queue_remove(*cond,
359						    _thread_run);
360
361						/* Check for no more waiters: */
362						if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
363							(*cond)->c_mutex = NULL;
364
365						/* Unock the condition variable structure: */
366						_SPINUNLOCK(&(*cond)->lock);
367
368						/* Return a timeout error: */
369						rval = ETIMEDOUT;
370
371						/*
372						 * Lock the mutex and ignore
373						 * any errors:
374						 */
375						(void)_mutex_cv_lock(mutex);
376					}
377				}
378			}
379			break;
380
381		/* Trap invalid condition variable types: */
382		default:
383			/* Unlock the condition variable structure: */
384			_SPINUNLOCK(&(*cond)->lock);
385
386			/* Return an invalid argument error: */
387			rval = EINVAL;
388			break;
389		}
390
391	}
392
393	/* Return the completion status: */
394	return (rval);
395}
396
397int
398pthread_cond_signal(pthread_cond_t * cond)
399{
400	int             rval = 0;
401	pthread_t       pthread;
402
403	if (cond == NULL || *cond == NULL)
404		rval = EINVAL;
405	else {
406		/*
407		 * Defer signals to protect the scheduling queues
408		 * from access by the signal handler:
409		 */
410		_thread_kern_sig_defer();
411
412		/* Lock the condition variable structure: */
413		_SPINLOCK(&(*cond)->lock);
414
415		/* Process according to condition variable type: */
416		switch ((*cond)->c_type) {
417		/* Fast condition variable: */
418		case COND_TYPE_FAST:
419			/*
420			 * Enter a loop to dequeue threads from the condition
421			 * queue until we find one that hasn't previously
422			 * timed out.
423			 */
424			while (((pthread = cond_queue_deq(*cond)) != NULL) &&
425			    (pthread->timeout != 0)) {
426			}
427
428			if (pthread != NULL)
429				/* Allow the thread to run: */
430				PTHREAD_NEW_STATE(pthread,PS_RUNNING);
431
432			/* Check for no more waiters: */
433			if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
434				(*cond)->c_mutex = NULL;
435			break;
436
437		/* Trap invalid condition variable types: */
438		default:
439			/* Return an invalid argument error: */
440			rval = EINVAL;
441			break;
442		}
443
444		/* Unlock the condition variable structure: */
445		_SPINUNLOCK(&(*cond)->lock);
446
447		/*
448		 * Undefer and handle pending signals, yielding if
449		 * necessary:
450		 */
451		_thread_kern_sig_undefer();
452	}
453
454	/* Return the completion status: */
455	return (rval);
456}
457
458int
459pthread_cond_broadcast(pthread_cond_t * cond)
460{
461	int             rval = 0;
462	pthread_t       pthread;
463
464	if (cond == NULL || *cond == NULL)
465		rval = EINVAL;
466	else {
467		/*
468		 * Defer signals to protect the scheduling queues
469		 * from access by the signal handler:
470		 */
471		_thread_kern_sig_defer();
472
473		/* Lock the condition variable structure: */
474		_SPINLOCK(&(*cond)->lock);
475
476		/* Process according to condition variable type: */
477		switch ((*cond)->c_type) {
478		/* Fast condition variable: */
479		case COND_TYPE_FAST:
480			/*
481			 * Enter a loop to bring all threads off the
482			 * condition queue:
483			 */
484			while ((pthread = cond_queue_deq(*cond)) != NULL) {
485				/*
486				 * The thread is already running if the
487				 * timeout flag is set.
488				 */
489				if (pthread->timeout == 0)
490					PTHREAD_NEW_STATE(pthread,PS_RUNNING);
491			}
492
493			/* There are no more waiting threads: */
494			(*cond)->c_mutex = NULL;
495			break;
496
497		/* Trap invalid condition variable types: */
498		default:
499			/* Return an invalid argument error: */
500			rval = EINVAL;
501			break;
502		}
503
504		/* Unlock the condition variable structure: */
505		_SPINUNLOCK(&(*cond)->lock);
506
507		/*
508		 * Undefer and handle pending signals, yielding if
509		 * necessary:
510		 */
511		_thread_kern_sig_undefer();
512	}
513
514	/* Return the completion status: */
515	return (rval);
516}
517
518/*
519 * Dequeue a waiting thread from the head of a condition queue in
520 * descending priority order.
521 */
522static inline pthread_t
523cond_queue_deq(pthread_cond_t cond)
524{
525	pthread_t pthread;
526
527	if ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
528		TAILQ_REMOVE(&cond->c_queue, pthread, qe);
529		pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
530	}
531
532	return(pthread);
533}
534
535/*
536 * Remove a waiting thread from a condition queue in descending priority
537 * order.
538 */
539static inline void
540cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
541{
542	/*
543	 * Because pthread_cond_timedwait() can timeout as well
544	 * as be signaled by another thread, it is necessary to
545	 * guard against removing the thread from the queue if
546	 * it isn't in the queue.
547	 */
548	if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) {
549		TAILQ_REMOVE(&cond->c_queue, pthread, qe);
550		pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
551	}
552}
553
554/*
555 * Enqueue a waiting thread to a condition queue in descending priority
556 * order.
557 */
558static inline void
559cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
560{
561	pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
562
563	/*
564	 * For the common case of all threads having equal priority,
565	 * we perform a quick check against the priority of the thread
566	 * at the tail of the queue.
567	 */
568	if ((tid == NULL) || (pthread->active_priority <= tid->active_priority))
569		TAILQ_INSERT_TAIL(&cond->c_queue, pthread, qe);
570	else {
571		tid = TAILQ_FIRST(&cond->c_queue);
572		while (pthread->active_priority <= tid->active_priority)
573			tid = TAILQ_NEXT(tid, qe);
574		TAILQ_INSERT_BEFORE(tid, pthread, qe);
575	}
576	pthread->flags |= PTHREAD_FLAGS_IN_CONDQ;
577}
578#endif
579