thr_cond.c revision 63364
1/*
2 * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by John Birrell.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $FreeBSD: head/lib/libkse/thread/thr_cond.c 63364 2000-07-18 01:38:19Z jasone $
33 */
34#include <stdlib.h>
35#include <errno.h>
36#include <string.h>
37#ifdef _THREAD_SAFE
38#include <pthread.h>
39#include "pthread_private.h"
40
41/*
42 * Prototypes
43 */
44static inline pthread_t	cond_queue_deq(pthread_cond_t);
45static inline void	cond_queue_remove(pthread_cond_t, pthread_t);
46static inline void	cond_queue_enq(pthread_cond_t, pthread_t);
47
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	interrupted = 0;
162
163	_thread_enter_cancellation_point();
164
165	if (cond == NULL)
166		rval = EINVAL;
167
168	/*
169	 * If the condition variable is statically initialized,
170	 * perform the dynamic initialization:
171	 */
172	else if (*cond != NULL ||
173	    (rval = pthread_cond_init(cond,NULL)) == 0) {
174
175		_thread_enter_cancellation_point();
176
177		/* Lock the condition variable structure: */
178		_SPINLOCK(&(*cond)->lock);
179
180		/*
181		 * 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 (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
309	    abstime->tv_nsec >= 1000000000)
310		rval = EINVAL;
311	/*
312	 * If the condition variable is statically initialized, perform dynamic
313	 * initialization.
314	 */
315	else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) {
316		_thread_enter_cancellation_point();
317
318		/* Lock the condition variable structure: */
319		_SPINLOCK(&(*cond)->lock);
320
321		/*
322		 * If the condvar was statically allocated, properly
323		 * initialize the tail queue.
324		 */
325		if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
326			TAILQ_INIT(&(*cond)->c_queue);
327			(*cond)->c_flags |= COND_FLAGS_INITED;
328		}
329
330		/* Process according to condition variable type: */
331		switch ((*cond)->c_type) {
332		/* Fast condition variable: */
333		case COND_TYPE_FAST:
334			if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
335			    ((*cond)->c_mutex != *mutex))) {
336				/* Return invalid argument error: */
337				rval = EINVAL;
338
339				/* Unlock the condition variable structure: */
340				_SPINUNLOCK(&(*cond)->lock);
341			} else {
342				/* Set the wakeup time: */
343				_thread_run->wakeup_time.tv_sec =
344				    abstime->tv_sec;
345				_thread_run->wakeup_time.tv_nsec =
346				    abstime->tv_nsec;
347
348				/* Reset the timeout and interrupted flags: */
349				_thread_run->timeout = 0;
350				_thread_run->interrupted = 0;
351
352				/*
353				 * Queue the running thread for the condition
354				 * variable:
355				 */
356				cond_queue_enq(*cond, _thread_run);
357
358				/* Remember the mutex that is being used: */
359				(*cond)->c_mutex = *mutex;
360
361				/* Unlock the mutex: */
362				if ((rval = _mutex_cv_unlock(mutex)) != 0) {
363					/*
364					 * Cannot unlock the mutex, so remove
365					 * the running thread from the condition
366					 * variable queue:
367					 */
368					cond_queue_remove(*cond, _thread_run);
369
370					/* Check for no more waiters: */
371					if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
372						(*cond)->c_mutex = NULL;
373
374					/* Unlock the condition variable structure: */
375					_SPINUNLOCK(&(*cond)->lock);
376				} else {
377					/*
378					 * Schedule the next thread and unlock
379					 * the condition variable structure:
380					 */
381					_thread_kern_sched_state_unlock(PS_COND_WAIT,
382				  	     &(*cond)->lock, __FILE__, __LINE__);
383
384					/*
385					 * Check if the wait timedout or was
386					 * interrupted (canceled):
387					 */
388					if ((_thread_run->timeout == 0) &&
389					    (_thread_run->interrupted == 0)) {
390						/* Lock the mutex: */
391						rval = _mutex_cv_lock(mutex);
392
393					} else {
394						/*
395						 * Remember if this thread was
396						 * interrupted:
397						 */
398						interrupted = _thread_run->interrupted;
399
400						/* Lock the condition variable structure: */
401						_SPINLOCK(&(*cond)->lock);
402
403						/*
404						 * The wait timed out; remove
405						 * the thread from the condition
406					 	 * variable queue:
407						 */
408						cond_queue_remove(*cond,
409						    _thread_run);
410
411						/* Check for no more waiters: */
412						if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
413							(*cond)->c_mutex = NULL;
414
415						/* Unock the condition variable structure: */
416						_SPINUNLOCK(&(*cond)->lock);
417
418						/* Return a timeout error: */
419						rval = ETIMEDOUT;
420
421						/*
422						 * Lock the mutex and ignore any
423						 * errors.  Note that even though
424						 * this thread may have been
425						 * canceled, POSIX requires that
426						 * the mutex be reaquired prior
427						 * to cancellation.
428						 */
429						(void)_mutex_cv_lock(mutex);
430					}
431				}
432			}
433			break;
434
435		/* Trap invalid condition variable types: */
436		default:
437			/* Unlock the condition variable structure: */
438			_SPINUNLOCK(&(*cond)->lock);
439
440			/* Return an invalid argument error: */
441			rval = EINVAL;
442			break;
443		}
444
445		if (interrupted != 0) {
446			if (_thread_run->continuation != NULL)
447				_thread_run->continuation((void *) _thread_run);
448		}
449
450		_thread_leave_cancellation_point();
451	}
452
453	_thread_leave_cancellation_point();
454
455	/* Return the completion status: */
456	return (rval);
457}
458
459int
460pthread_cond_signal(pthread_cond_t * cond)
461{
462	int             rval = 0;
463	pthread_t       pthread;
464
465	if (cond == NULL)
466		rval = EINVAL;
467       /*
468        * If the condition variable is statically initialized, perform dynamic
469        * initialization.
470        */
471	else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL) == 0)) {
472		/*
473		 * Defer signals to protect the scheduling queues
474		 * from access by the signal handler:
475		 */
476		_thread_kern_sig_defer();
477
478		/* Lock the condition variable structure: */
479		_SPINLOCK(&(*cond)->lock);
480
481		/* Process according to condition variable type: */
482		switch ((*cond)->c_type) {
483		/* Fast condition variable: */
484		case COND_TYPE_FAST:
485			if ((pthread = cond_queue_deq(*cond)) != NULL) {
486				/*
487				 * Unless the thread is currently suspended,
488				 * allow it to run.  If the thread is suspended,
489				 * make a note that the thread isn't in a wait
490				 * queue any more.
491				 */
492				if (pthread->state != PS_SUSPENDED)
493					PTHREAD_NEW_STATE(pthread,PS_RUNNING);
494				else
495					pthread->suspended = SUSP_NOWAIT;
496			}
497
498			/* Check for no more waiters: */
499			if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
500				(*cond)->c_mutex = NULL;
501			break;
502
503		/* Trap invalid condition variable types: */
504		default:
505			/* Return an invalid argument error: */
506			rval = EINVAL;
507			break;
508		}
509
510		/* Unlock the condition variable structure: */
511		_SPINUNLOCK(&(*cond)->lock);
512
513		/*
514		 * Undefer and handle pending signals, yielding if
515		 * necessary:
516		 */
517		_thread_kern_sig_undefer();
518	}
519
520	/* Return the completion status: */
521	return (rval);
522}
523
524int
525pthread_cond_broadcast(pthread_cond_t * cond)
526{
527	int             rval = 0;
528	pthread_t       pthread;
529
530	if (cond == NULL)
531		rval = EINVAL;
532       /*
533        * If the condition variable is statically initialized, perform dynamic
534        * initialization.
535        */
536	else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL) == 0)) {
537		/*
538		 * Defer signals to protect the scheduling queues
539		 * from access by the signal handler:
540		 */
541		_thread_kern_sig_defer();
542
543		/* Lock the condition variable structure: */
544		_SPINLOCK(&(*cond)->lock);
545
546		/* Process according to condition variable type: */
547		switch ((*cond)->c_type) {
548		/* Fast condition variable: */
549		case COND_TYPE_FAST:
550			/*
551			 * Enter a loop to bring all threads off the
552			 * condition queue:
553			 */
554			while ((pthread = cond_queue_deq(*cond)) != NULL) {
555				/*
556				 * Unless the thread is currently suspended,
557				 * allow it to run.  If the thread is suspended,
558				 * make a note that the thread isn't in a wait
559				 * queue any more.
560				 */
561				if (pthread->state != PS_SUSPENDED)
562					PTHREAD_NEW_STATE(pthread,PS_RUNNING);
563				else
564					pthread->suspended = SUSP_NOWAIT;
565			}
566
567			/* There are no more waiting threads: */
568			(*cond)->c_mutex = NULL;
569			break;
570
571		/* Trap invalid condition variable types: */
572		default:
573			/* Return an invalid argument error: */
574			rval = EINVAL;
575			break;
576		}
577
578		/* Unlock the condition variable structure: */
579		_SPINUNLOCK(&(*cond)->lock);
580
581		/*
582		 * Undefer and handle pending signals, yielding if
583		 * necessary:
584		 */
585		_thread_kern_sig_undefer();
586	}
587
588	/* Return the completion status: */
589	return (rval);
590}
591
592/*
593 * Dequeue a waiting thread from the head of a condition queue in
594 * descending priority order.
595 */
596static inline pthread_t
597cond_queue_deq(pthread_cond_t cond)
598{
599	pthread_t pthread;
600
601	while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
602		TAILQ_REMOVE(&cond->c_queue, pthread, qe);
603		pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
604		if ((pthread->timeout == 0) && (pthread->interrupted == 0))
605			/*
606			 * Only exit the loop when we find a thread
607			 * that hasn't timed out or been canceled;
608			 * those threads are already running and don't
609			 * need their run state changed.
610			 */
611			break;
612	}
613
614	return(pthread);
615}
616
617/*
618 * Remove a waiting thread from a condition queue in descending priority
619 * order.
620 */
621static inline void
622cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
623{
624	/*
625	 * Because pthread_cond_timedwait() can timeout as well
626	 * as be signaled by another thread, it is necessary to
627	 * guard against removing the thread from the queue if
628	 * it isn't in the queue.
629	 */
630	if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) {
631		TAILQ_REMOVE(&cond->c_queue, pthread, qe);
632		pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
633	}
634}
635
636/*
637 * Enqueue a waiting thread to a condition queue in descending priority
638 * order.
639 */
640static inline void
641cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
642{
643	pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
644
645	/*
646	 * For the common case of all threads having equal priority,
647	 * we perform a quick check against the priority of the thread
648	 * at the tail of the queue.
649	 */
650	if ((tid == NULL) || (pthread->active_priority <= tid->active_priority))
651		TAILQ_INSERT_TAIL(&cond->c_queue, pthread, qe);
652	else {
653		tid = TAILQ_FIRST(&cond->c_queue);
654		while (pthread->active_priority <= tid->active_priority)
655			tid = TAILQ_NEXT(tid, qe);
656		TAILQ_INSERT_BEFORE(tid, pthread, qe);
657	}
658	pthread->flags |= PTHREAD_FLAGS_IN_CONDQ;
659}
660#endif
661