thr_cond.c revision 61681
1239310Sdim/*
2239310Sdim * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
3239310Sdim * All rights reserved.
4239310Sdim *
5239310Sdim * Redistribution and use in source and binary forms, with or without
6239310Sdim * modification, are permitted provided that the following conditions
7239310Sdim * are met:
8239310Sdim * 1. Redistributions of source code must retain the above copyright
9239310Sdim *    notice, this list of conditions and the following disclaimer.
10239310Sdim * 2. Redistributions in binary form must reproduce the above copyright
11239310Sdim *    notice, this list of conditions and the following disclaimer in the
12239310Sdim *    documentation and/or other materials provided with the distribution.
13239310Sdim * 3. All advertising materials mentioning features or use of this software
14239310Sdim *    must display the following acknowledgement:
15249423Sdim *	This product includes software developed by John Birrell.
16239310Sdim * 4. Neither the name of the author nor the names of any co-contributors
17249423Sdim *    may be used to endorse or promote products derived from this software
18249423Sdim *    without specific prior written permission.
19239310Sdim *
20249423Sdim * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
21239310Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22239310Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23239310Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24239310Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25239310Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26239310Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27239310Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28249423Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29239310Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30239310Sdim * SUCH DAMAGE.
31239310Sdim *
32239310Sdim * $FreeBSD: head/lib/libkse/thread/thr_cond.c 61681 2000-06-14 17:17:41Z jasone $
33249423Sdim */
34249423Sdim#include <stdlib.h>
35249423Sdim#include <errno.h>
36249423Sdim#include <string.h>
37239310Sdim#ifdef _THREAD_SAFE
38239310Sdim#include <pthread.h>
39239310Sdim#include "pthread_private.h"
40239310Sdim
41239310Sdim/*
42239310Sdim * Prototypes
43239310Sdim */
44239310Sdimstatic inline pthread_t	cond_queue_deq(pthread_cond_t);
45239310Sdimstatic inline void	cond_queue_remove(pthread_cond_t, pthread_t);
46239310Sdimstatic inline void	cond_queue_enq(pthread_cond_t, pthread_t);
47239310Sdim
48239310Sdim/* Reinitialize a condition variable to defaults. */
49239310Sdimint
50249423Sdim_cond_reinit(pthread_cond_t * cond)
51249423Sdim{
52251662Sdim	int ret = 0;
53249423Sdim
54239310Sdim	if (cond == NULL)
55239310Sdim		ret = EINVAL;
56239310Sdim	else if (*cond == NULL)
57239310Sdim		ret = pthread_cond_init(cond, NULL);
58239310Sdim	else {
59239310Sdim		/*
60249423Sdim		 * Initialize the condition variable structure:
61249423Sdim		 */
62249423Sdim		TAILQ_INIT(&(*cond)->c_queue);
63251662Sdim		(*cond)->c_flags = COND_FLAGS_INITED;
64239310Sdim		(*cond)->c_type = COND_TYPE_FAST;
65239310Sdim		(*cond)->c_mutex = NULL;
66249423Sdim		memset(&(*cond)->lock, 0, sizeof((*cond)->lock));
67249423Sdim	}
68249423Sdim	return (ret);
69249423Sdim}
70249423Sdim
71249423Sdimint
72249423Sdimpthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
73249423Sdim{
74263508Sdim	enum pthread_cond_type type;
75263508Sdim	pthread_cond_t	pcond;
76263508Sdim	int             rval = 0;
77239310Sdim
78239310Sdim	if (cond == NULL)
79239310Sdim		rval = EINVAL;
80249423Sdim	else {
81249423Sdim		/*
82249423Sdim		 * Check if a pointer to a condition variable attribute
83249423Sdim		 * structure was passed by the caller:
84249423Sdim		 */
85239310Sdim		if (cond_attr != NULL && *cond_attr != NULL) {
86239310Sdim			/* Default to a fast condition variable: */
87239310Sdim			type = (*cond_attr)->c_type;
88249423Sdim		} else {
89249423Sdim			/* Default to a fast condition variable: */
90249423Sdim			type = COND_TYPE_FAST;
91249423Sdim		}
92249423Sdim
93239310Sdim		/* Process according to condition variable type: */
94263508Sdim		switch (type) {
95239310Sdim		/* Fast condition variable: */
96239310Sdim		case COND_TYPE_FAST:
97239310Sdim			/* Nothing to do here. */
98249423Sdim			break;
99239310Sdim
100239310Sdim		/* Trap invalid condition variable types: */
101239310Sdim		default:
102239310Sdim			/* Return an invalid argument error: */
103239310Sdim			rval = EINVAL;
104251662Sdim			break;
105239310Sdim		}
106239310Sdim
107263508Sdim		/* Check for no errors: */
108263508Sdim		if (rval == 0) {
109263508Sdim			if ((pcond = (pthread_cond_t)
110263508Sdim			    malloc(sizeof(struct pthread_cond))) == NULL) {
111263508Sdim				rval = ENOMEM;
112239310Sdim			} else {
113263508Sdim				/*
114239310Sdim				 * Initialise the condition variable
115239310Sdim				 * structure:
116239310Sdim				 */
117239310Sdim				TAILQ_INIT(&pcond->c_queue);
118239310Sdim				pcond->c_flags |= COND_FLAGS_INITED;
119239310Sdim				pcond->c_type = type;
120251662Sdim				pcond->c_mutex = NULL;
121263508Sdim				memset(&pcond->lock,0,sizeof(pcond->lock));
122263508Sdim				*cond = pcond;
123263508Sdim			}
124263508Sdim		}
125263508Sdim	}
126263508Sdim	/* Return the completion status: */
127263508Sdim	return (rval);
128263508Sdim}
129263508Sdim
130263508Sdimint
131251662Sdimpthread_cond_destroy(pthread_cond_t * cond)
132251662Sdim{
133251662Sdim	int             rval = 0;
134251662Sdim
135239310Sdim	if (cond == NULL || *cond == NULL)
136239310Sdim		rval = EINVAL;
137239310Sdim	else {
138239310Sdim		/* Lock the condition variable structure: */
139239310Sdim		_SPINLOCK(&(*cond)->lock);
140239310Sdim
141239310Sdim		/*
142239310Sdim		 * Free the memory allocated for the condition
143249423Sdim		 * variable structure:
144263508Sdim		 */
145263508Sdim		free(*cond);
146263508Sdim
147263508Sdim		/*
148263508Sdim		 * NULL the caller's pointer now that the condition
149263508Sdim		 * variable has been destroyed:
150263508Sdim		 */
151263508Sdim		*cond = NULL;
152263508Sdim	}
153263508Sdim	/* Return the completion status: */
154263508Sdim	return (rval);
155263508Sdim}
156263508Sdim
157263508Sdimint
158263508Sdimpthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
159263508Sdim{
160263508Sdim	int	rval = 0;
161263508Sdim	int	interrupted = 0;
162263508Sdim
163263508Sdim	_thread_enter_cancellation_point();
164263508Sdim
165263508Sdim	if (cond == NULL)
166263508Sdim		rval = EINVAL;
167263508Sdim
168263508Sdim	/*
169263508Sdim	 * If the condition variable is statically initialized,
170263508Sdim	 * perform the dynamic initialization:
171263508Sdim	 */
172263508Sdim	else if (*cond != NULL ||
173263508Sdim	    (rval = pthread_cond_init(cond,NULL)) == 0) {
174263508Sdim
175263508Sdim		_thread_enter_cancellation_point();
176263508Sdim
177263508Sdim		/* Lock the condition variable structure: */
178263508Sdim		_SPINLOCK(&(*cond)->lock);
179263508Sdim
180263508Sdim		/*
181263508Sdim		 * If the condvar was statically allocated, properly
182		 * initialize the tail queue.
183		 */
184		if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
185			TAILQ_INIT(&(*cond)->c_queue);
186			(*cond)->c_flags |= COND_FLAGS_INITED;
187		}
188
189		/* Process according to condition variable type: */
190		switch ((*cond)->c_type) {
191		/* Fast condition variable: */
192		case COND_TYPE_FAST:
193			if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
194			    ((*cond)->c_mutex != *mutex))) {
195				/* Unlock the condition variable structure: */
196				_SPINUNLOCK(&(*cond)->lock);
197
198				/* Return invalid argument error: */
199				rval = EINVAL;
200			} else {
201				/* Reset the timeout and interrupted flags: */
202				_thread_run->timeout = 0;
203				_thread_run->interrupted = 0;
204
205				/*
206				 * Queue the running thread for the condition
207				 * variable:
208				 */
209				cond_queue_enq(*cond, _thread_run);
210
211				/* Remember the mutex that is being used: */
212				(*cond)->c_mutex = *mutex;
213
214				/* Wait forever: */
215				_thread_run->wakeup_time.tv_sec = -1;
216
217				/* Unlock the mutex: */
218				if ((rval = _mutex_cv_unlock(mutex)) != 0) {
219					/*
220					 * Cannot unlock the mutex, so remove
221					 * the running thread from the condition
222					 * variable queue:
223					 */
224					cond_queue_remove(*cond, _thread_run);
225
226					/* Check for no more waiters: */
227					if (TAILQ_FIRST(&(*cond)->c_queue) ==
228					    NULL)
229						(*cond)->c_mutex = NULL;
230
231					/* Unlock the condition variable structure: */
232					_SPINUNLOCK(&(*cond)->lock);
233				}
234				else {
235					/*
236					 * Schedule the next thread and unlock
237					 * the condition variable structure:
238					 */
239					_thread_kern_sched_state_unlock(PS_COND_WAIT,
240				    	    &(*cond)->lock, __FILE__, __LINE__);
241
242					if (_thread_run->interrupted != 0) {
243						/*
244						 * Remember that this thread
245						 * was interrupted:
246						 */
247						interrupted = 1;
248
249						/*
250						 * Lock the condition variable
251						 * while removing the thread.
252						 */
253						_SPINLOCK(&(*cond)->lock);
254
255						cond_queue_remove(*cond,
256						    _thread_run);
257
258						/* Check for no more waiters: */
259						if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
260							(*cond)->c_mutex = NULL;
261
262						_SPINUNLOCK(&(*cond)->lock);
263					}
264
265					/*
266					 * Note that even though this thread may have
267					 * been canceled, POSIX requires that the mutex
268					 * be reaquired prior to cancellation.
269					 */
270					rval = _mutex_cv_lock(mutex);
271				}
272			}
273			break;
274
275		/* Trap invalid condition variable types: */
276		default:
277			/* Unlock the condition variable structure: */
278			_SPINUNLOCK(&(*cond)->lock);
279
280			/* Return an invalid argument error: */
281			rval = EINVAL;
282			break;
283		}
284
285		if (interrupted != 0) {
286			if (_thread_run->continuation != NULL)
287				_thread_run->continuation((void *) _thread_run);
288		}
289
290		_thread_leave_cancellation_point();
291	}
292
293	_thread_leave_cancellation_point();
294
295	/* Return the completion status: */
296	return (rval);
297}
298
299int
300pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
301		       const struct timespec * abstime)
302{
303	int	rval = 0;
304	int	interrupted = 0;
305
306	_thread_enter_cancellation_point();
307
308	if (cond == NULL || abstime == NULL)
309		rval = EINVAL;
310
311	if (abstime->tv_sec < 0 ||
312		abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
313		errno = EINVAL;
314		_thread_leave_cancellation_point();
315		return (-1);
316	}
317
318	/*
319	 * If the condition variable is statically initialized,
320	 * perform the dynamic initialization:
321	 */
322	if (*cond != NULL ||
323	    (rval = pthread_cond_init(cond,NULL)) == 0) {
324
325		_thread_enter_cancellation_point();
326
327		/* Lock the condition variable structure: */
328		_SPINLOCK(&(*cond)->lock);
329
330		/*
331		 * If the condvar was statically allocated, properly
332		 * initialize the tail queue.
333		 */
334		if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
335			TAILQ_INIT(&(*cond)->c_queue);
336			(*cond)->c_flags |= COND_FLAGS_INITED;
337		}
338
339		/* Process according to condition variable type: */
340		switch ((*cond)->c_type) {
341		/* Fast condition variable: */
342		case COND_TYPE_FAST:
343			if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
344			    ((*cond)->c_mutex != *mutex))) {
345				/* Return invalid argument error: */
346				rval = EINVAL;
347
348				/* Unlock the condition variable structure: */
349				_SPINUNLOCK(&(*cond)->lock);
350			} else {
351				/* Set the wakeup time: */
352				_thread_run->wakeup_time.tv_sec =
353				    abstime->tv_sec;
354				_thread_run->wakeup_time.tv_nsec =
355				    abstime->tv_nsec;
356
357				/* Reset the timeout and interrupted flags: */
358				_thread_run->timeout = 0;
359				_thread_run->interrupted = 0;
360
361				/*
362				 * Queue the running thread for the condition
363				 * variable:
364				 */
365				cond_queue_enq(*cond, _thread_run);
366
367				/* Remember the mutex that is being used: */
368				(*cond)->c_mutex = *mutex;
369
370				/* Unlock the mutex: */
371				if ((rval = _mutex_cv_unlock(mutex)) != 0) {
372					/*
373					 * Cannot unlock the mutex, so remove
374					 * the running thread from the condition
375					 * variable queue:
376					 */
377					cond_queue_remove(*cond, _thread_run);
378
379					/* Check for no more waiters: */
380					if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
381						(*cond)->c_mutex = NULL;
382
383					/* Unlock the condition variable structure: */
384					_SPINUNLOCK(&(*cond)->lock);
385				} else {
386					/*
387					 * Schedule the next thread and unlock
388					 * the condition variable structure:
389					 */
390					_thread_kern_sched_state_unlock(PS_COND_WAIT,
391				  	     &(*cond)->lock, __FILE__, __LINE__);
392
393					/*
394					 * Check if the wait timedout or was
395					 * interrupted (canceled):
396					 */
397					if ((_thread_run->timeout == 0) &&
398					    (_thread_run->interrupted == 0)) {
399						/* Lock the mutex: */
400						rval = _mutex_cv_lock(mutex);
401
402					} else {
403						/*
404						 * Remember if this thread was
405						 * interrupted:
406						 */
407						interrupted = _thread_run->interrupted;
408
409						/* Lock the condition variable structure: */
410						_SPINLOCK(&(*cond)->lock);
411
412						/*
413						 * The wait timed out; remove
414						 * the thread from the condition
415					 	 * variable queue:
416						 */
417						cond_queue_remove(*cond,
418						    _thread_run);
419
420						/* Check for no more waiters: */
421						if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
422							(*cond)->c_mutex = NULL;
423
424						/* Unock the condition variable structure: */
425						_SPINUNLOCK(&(*cond)->lock);
426
427						/* Return a timeout error: */
428						rval = ETIMEDOUT;
429
430						/*
431						 * Lock the mutex and ignore any
432						 * errors.  Note that even though
433						 * this thread may have been
434						 * canceled, POSIX requires that
435						 * the mutex be reaquired prior
436						 * to cancellation.
437						 */
438						(void)_mutex_cv_lock(mutex);
439					}
440				}
441			}
442			break;
443
444		/* Trap invalid condition variable types: */
445		default:
446			/* Unlock the condition variable structure: */
447			_SPINUNLOCK(&(*cond)->lock);
448
449			/* Return an invalid argument error: */
450			rval = EINVAL;
451			break;
452		}
453
454		if (interrupted != 0) {
455			if (_thread_run->continuation != NULL)
456				_thread_run->continuation((void *) _thread_run);
457		}
458
459		_thread_leave_cancellation_point();
460	}
461
462	_thread_leave_cancellation_point();
463
464	/* Return the completion status: */
465	return (rval);
466}
467
468int
469pthread_cond_signal(pthread_cond_t * cond)
470{
471	int             rval = 0;
472	pthread_t       pthread;
473
474	if (cond == NULL || *cond == NULL)
475		rval = EINVAL;
476	else {
477		/*
478		 * Defer signals to protect the scheduling queues
479		 * from access by the signal handler:
480		 */
481		_thread_kern_sig_defer();
482
483		/* Lock the condition variable structure: */
484		_SPINLOCK(&(*cond)->lock);
485
486		/* Process according to condition variable type: */
487		switch ((*cond)->c_type) {
488		/* Fast condition variable: */
489		case COND_TYPE_FAST:
490			if ((pthread = cond_queue_deq(*cond)) != NULL) {
491				/*
492				 * Unless the thread is currently suspended,
493				 * allow it to run.  If the thread is suspended,
494				 * make a note that the thread isn't in a wait
495				 * queue any more.
496				 */
497				if (pthread->state != PS_SUSPENDED)
498					PTHREAD_NEW_STATE(pthread,PS_RUNNING);
499				else
500					pthread->suspended = SUSP_NOWAIT;
501			}
502
503			/* Check for no more waiters: */
504			if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
505				(*cond)->c_mutex = NULL;
506			break;
507
508		/* Trap invalid condition variable types: */
509		default:
510			/* Return an invalid argument error: */
511			rval = EINVAL;
512			break;
513		}
514
515		/* Unlock the condition variable structure: */
516		_SPINUNLOCK(&(*cond)->lock);
517
518		/*
519		 * Undefer and handle pending signals, yielding if
520		 * necessary:
521		 */
522		_thread_kern_sig_undefer();
523	}
524
525	/* Return the completion status: */
526	return (rval);
527}
528
529int
530pthread_cond_broadcast(pthread_cond_t * cond)
531{
532	int             rval = 0;
533	pthread_t       pthread;
534
535	if (cond == NULL || *cond == NULL)
536		rval = EINVAL;
537	else {
538		/*
539		 * Defer signals to protect the scheduling queues
540		 * from access by the signal handler:
541		 */
542		_thread_kern_sig_defer();
543
544		/* Lock the condition variable structure: */
545		_SPINLOCK(&(*cond)->lock);
546
547		/* Process according to condition variable type: */
548		switch ((*cond)->c_type) {
549		/* Fast condition variable: */
550		case COND_TYPE_FAST:
551			/*
552			 * Enter a loop to bring all threads off the
553			 * condition queue:
554			 */
555			while ((pthread = cond_queue_deq(*cond)) != NULL) {
556				/*
557				 * Unless the thread is currently suspended,
558				 * allow it to run.  If the thread is suspended,
559				 * make a note that the thread isn't in a wait
560				 * queue any more.
561				 */
562				if (pthread->state != PS_SUSPENDED)
563					PTHREAD_NEW_STATE(pthread,PS_RUNNING);
564				else
565					pthread->suspended = SUSP_NOWAIT;
566			}
567
568			/* There are no more waiting threads: */
569			(*cond)->c_mutex = NULL;
570			break;
571
572		/* Trap invalid condition variable types: */
573		default:
574			/* Return an invalid argument error: */
575			rval = EINVAL;
576			break;
577		}
578
579		/* Unlock the condition variable structure: */
580		_SPINUNLOCK(&(*cond)->lock);
581
582		/*
583		 * Undefer and handle pending signals, yielding if
584		 * necessary:
585		 */
586		_thread_kern_sig_undefer();
587	}
588
589	/* Return the completion status: */
590	return (rval);
591}
592
593/*
594 * Dequeue a waiting thread from the head of a condition queue in
595 * descending priority order.
596 */
597static inline pthread_t
598cond_queue_deq(pthread_cond_t cond)
599{
600	pthread_t pthread;
601
602	while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
603		TAILQ_REMOVE(&cond->c_queue, pthread, qe);
604		pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
605		if ((pthread->timeout == 0) && (pthread->interrupted == 0))
606			/*
607			 * Only exit the loop when we find a thread
608			 * that hasn't timed out or been canceled;
609			 * those threads are already running and don't
610			 * need their run state changed.
611			 */
612			break;
613	}
614
615	return(pthread);
616}
617
618/*
619 * Remove a waiting thread from a condition queue in descending priority
620 * order.
621 */
622static inline void
623cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
624{
625	/*
626	 * Because pthread_cond_timedwait() can timeout as well
627	 * as be signaled by another thread, it is necessary to
628	 * guard against removing the thread from the queue if
629	 * it isn't in the queue.
630	 */
631	if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) {
632		TAILQ_REMOVE(&cond->c_queue, pthread, qe);
633		pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
634	}
635}
636
637/*
638 * Enqueue a waiting thread to a condition queue in descending priority
639 * order.
640 */
641static inline void
642cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
643{
644	pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
645
646	/*
647	 * For the common case of all threads having equal priority,
648	 * we perform a quick check against the priority of the thread
649	 * at the tail of the queue.
650	 */
651	if ((tid == NULL) || (pthread->active_priority <= tid->active_priority))
652		TAILQ_INSERT_TAIL(&cond->c_queue, pthread, qe);
653	else {
654		tid = TAILQ_FIRST(&cond->c_queue);
655		while (pthread->active_priority <= tid->active_priority)
656			tid = TAILQ_NEXT(tid, qe);
657		TAILQ_INSERT_BEFORE(tid, pthread, qe);
658	}
659	pthread->flags |= PTHREAD_FLAGS_IN_CONDQ;
660}
661#endif
662