thr_cond.c revision 129484
1112918Sjeff/*
2112918Sjeff * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
3112918Sjeff * All rights reserved.
4112918Sjeff *
5112918Sjeff * Redistribution and use in source and binary forms, with or without
6112918Sjeff * modification, are permitted provided that the following conditions
7112918Sjeff * are met:
8112918Sjeff * 1. Redistributions of source code must retain the above copyright
9112918Sjeff *    notice, this list of conditions and the following disclaimer.
10112918Sjeff * 2. Redistributions in binary form must reproduce the above copyright
11112918Sjeff *    notice, this list of conditions and the following disclaimer in the
12112918Sjeff *    documentation and/or other materials provided with the distribution.
13112918Sjeff * 3. All advertising materials mentioning features or use of this software
14112918Sjeff *    must display the following acknowledgement:
15112918Sjeff *	This product includes software developed by John Birrell.
16112918Sjeff * 4. Neither the name of the author nor the names of any co-contributors
17112918Sjeff *    may be used to endorse or promote products derived from this software
18112918Sjeff *    without specific prior written permission.
19112918Sjeff *
20112918Sjeff * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
21112918Sjeff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22112918Sjeff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23112918Sjeff * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24112918Sjeff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25112918Sjeff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26112918Sjeff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27112918Sjeff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28112918Sjeff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29112918Sjeff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30112918Sjeff * SUCH DAMAGE.
31112918Sjeff *
32112918Sjeff * $FreeBSD: head/lib/libthr/thread/thr_cond.c 129484 2004-05-20 12:06:16Z mtm $
33112918Sjeff */
34112918Sjeff#include <stdlib.h>
35112918Sjeff#include <errno.h>
36112918Sjeff#include <string.h>
37112918Sjeff#include <pthread.h>
38112918Sjeff#include "thr_private.h"
39112918Sjeff
40112918Sjeff/*
41115389Smtm * Proctect two different threads calling a pthread_cond_* function
42115389Smtm * from accidentally initializing the condition variable twice.
43115389Smtm */
44115389Smtmstatic spinlock_t static_cond_lock = _SPINLOCK_INITIALIZER;
45115389Smtm
46115389Smtm/*
47112918Sjeff * Prototypes
48112918Sjeff */
49115389Smtmstatic inline int	cond_init(pthread_cond_t *);
50112918Sjeffstatic pthread_t	cond_queue_deq(pthread_cond_t);
51112918Sjeffstatic void		cond_queue_remove(pthread_cond_t, pthread_t);
52112918Sjeffstatic void		cond_queue_enq(pthread_cond_t, pthread_t);
53115277Smtmstatic int		cond_signal(pthread_cond_t *, int);
54115035Smtmstatic int		cond_wait_common(pthread_cond_t *,
55115035Smtm			    pthread_mutex_t *, const struct timespec *);
56112918Sjeff
57112918Sjeff__weak_reference(_pthread_cond_init, pthread_cond_init);
58112918Sjeff__weak_reference(_pthread_cond_destroy, pthread_cond_destroy);
59112918Sjeff__weak_reference(_pthread_cond_wait, pthread_cond_wait);
60112918Sjeff__weak_reference(_pthread_cond_timedwait, pthread_cond_timedwait);
61112918Sjeff__weak_reference(_pthread_cond_signal, pthread_cond_signal);
62112918Sjeff__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
63112918Sjeff
64112918Sjeff#define	COND_LOCK(c)						\
65112918Sjeffdo {								\
66112918Sjeff	if (umtx_lock(&(c)->c_lock, curthread->thr_id))		\
67112918Sjeff		abort();					\
68112918Sjeff} while (0)
69112918Sjeff
70112918Sjeff#define	COND_UNLOCK(c)						\
71112918Sjeffdo {								\
72112918Sjeff	if (umtx_unlock(&(c)->c_lock, curthread->thr_id))	\
73112918Sjeff		abort();					\
74112918Sjeff} while (0)
75112918Sjeff
76112918Sjeff
77112918Sjeff/* Reinitialize a condition variable to defaults. */
78112918Sjeffint
79112918Sjeff_cond_reinit(pthread_cond_t *cond)
80112918Sjeff{
81112918Sjeff	if (cond == NULL)
82112918Sjeff		return (EINVAL);
83112918Sjeff
84112918Sjeff	if (*cond == NULL)
85112918Sjeff		return (pthread_cond_init(cond, NULL));
86112918Sjeff
87112918Sjeff	/*
88112918Sjeff	 * Initialize the condition variable structure:
89112918Sjeff	 */
90112918Sjeff	TAILQ_INIT(&(*cond)->c_queue);
91112918Sjeff	(*cond)->c_flags = COND_FLAGS_INITED;
92112918Sjeff	(*cond)->c_type = COND_TYPE_FAST;
93112918Sjeff	(*cond)->c_mutex = NULL;
94112918Sjeff	(*cond)->c_seqno = 0;
95112918Sjeff	bzero(&(*cond)->c_lock, sizeof((*cond)->c_lock));
96112918Sjeff
97112918Sjeff	return (0);
98112918Sjeff}
99112918Sjeff
100112918Sjeffint
101112918Sjeff_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
102112918Sjeff{
103112918Sjeff	enum pthread_cond_type type;
104112918Sjeff	pthread_cond_t	pcond;
105112918Sjeff
106112918Sjeff	if (cond == NULL)
107112918Sjeff		return (EINVAL);
108112918Sjeff
109112918Sjeff	/*
110112918Sjeff	 * Check if a pointer to a condition variable attribute
111112918Sjeff	 * structure was passed by the caller:
112112918Sjeff	 */
113112918Sjeff	if (cond_attr != NULL && *cond_attr != NULL)
114112918Sjeff		type = (*cond_attr)->c_type;
115112918Sjeff	else
116112918Sjeff		/* Default to a fast condition variable: */
117112918Sjeff		type = COND_TYPE_FAST;
118112918Sjeff
119112918Sjeff	/* Process according to condition variable type: */
120112918Sjeff	switch (type) {
121112918Sjeff	case COND_TYPE_FAST:
122112918Sjeff		break;
123112918Sjeff	default:
124112918Sjeff		return (EINVAL);
125112918Sjeff		break;
126112918Sjeff	}
127112918Sjeff
128112918Sjeff	if ((pcond = (pthread_cond_t)
129112918Sjeff	    malloc(sizeof(struct pthread_cond))) == NULL)
130112918Sjeff		return (ENOMEM);
131112918Sjeff	/*
132112918Sjeff	 * Initialise the condition variable
133112918Sjeff	 * structure:
134112918Sjeff	 */
135112918Sjeff	TAILQ_INIT(&pcond->c_queue);
136112918Sjeff	pcond->c_flags |= COND_FLAGS_INITED;
137112918Sjeff	pcond->c_type = type;
138112918Sjeff	pcond->c_mutex = NULL;
139112918Sjeff	pcond->c_seqno = 0;
140112918Sjeff	bzero(&pcond->c_lock, sizeof(pcond->c_lock));
141112918Sjeff
142112918Sjeff	*cond = pcond;
143112918Sjeff
144112918Sjeff	return (0);
145112918Sjeff}
146112918Sjeff
147112918Sjeffint
148112918Sjeff_pthread_cond_destroy(pthread_cond_t *cond)
149112918Sjeff{
150127566Smtm	/*
151127566Smtm	 * Short circuit for a statically initialized condvar
152127566Smtm	 * that is being destroyed without having been used.
153127566Smtm	 */
154127566Smtm	if (*cond == PTHREAD_COND_INITIALIZER)
155127566Smtm		return (0);
156112918Sjeff
157112918Sjeff	COND_LOCK(*cond);
158112918Sjeff
159112918Sjeff	/*
160112918Sjeff	 * Free the memory allocated for the condition
161112918Sjeff	 * variable structure:
162112918Sjeff	 */
163112918Sjeff	free(*cond);
164112918Sjeff
165112918Sjeff	/*
166112918Sjeff	 * NULL the caller's pointer now that the condition
167112918Sjeff	 * variable has been destroyed:
168112918Sjeff	 */
169112918Sjeff	*cond = NULL;
170112918Sjeff
171112918Sjeff	return (0);
172112918Sjeff}
173112918Sjeff
174112918Sjeffint
175112918Sjeff_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
176112918Sjeff{
177112918Sjeff	int rval;
178112918Sjeff
179115035Smtm	rval = cond_wait_common(cond, mutex, NULL);
180112918Sjeff
181112918Sjeff	/* This should never happen. */
182112918Sjeff	if (rval == ETIMEDOUT)
183112918Sjeff		abort();
184112918Sjeff
185112918Sjeff	return (rval);
186112918Sjeff}
187112918Sjeff
188112918Sjeffint
189112918Sjeff_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
190112918Sjeff		       const struct timespec * abstime)
191112918Sjeff{
192115035Smtm	if (abstime == NULL || abstime->tv_nsec >= 1000000000)
193115035Smtm		return (EINVAL);
194115035Smtm
195115035Smtm	return (cond_wait_common(cond, mutex, abstime));
196115035Smtm}
197115035Smtm
198115035Smtmstatic int
199115035Smtmcond_wait_common(pthread_cond_t * cond, pthread_mutex_t * mutex,
200115035Smtm	         const struct timespec * abstime)
201115035Smtm{
202112918Sjeff	int	rval = 0;
203112918Sjeff	int	mtxrval;
204112918Sjeff
205112918Sjeff
206115277Smtm	if (cond == NULL)
207115277Smtm		return (EINVAL);
208112918Sjeff	/*
209112918Sjeff	 * If the condition variable is statically initialized, perform dynamic
210112918Sjeff	 * initialization.
211112918Sjeff	 */
212115389Smtm	if (*cond == PTHREAD_COND_INITIALIZER && (rval = cond_init(cond)) != 0)
213112918Sjeff		return (rval);
214112918Sjeff
215129484Smtm	if ((*cond)->c_type != COND_TYPE_FAST)
216129484Smtm		return (EINVAL);
217112918Sjeff
218112918Sjeff	COND_LOCK(*cond);
219112918Sjeff
220112918Sjeff	/*
221112918Sjeff	 * If the condvar was statically allocated, properly
222112918Sjeff	 * initialize the tail queue.
223112918Sjeff	 */
224112918Sjeff	if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
225112918Sjeff		TAILQ_INIT(&(*cond)->c_queue);
226112918Sjeff		(*cond)->c_flags |= COND_FLAGS_INITED;
227112918Sjeff	}
228112918Sjeff
229129484Smtm	if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
230129484Smtm	    ((*cond)->c_mutex != *mutex))) {
231129484Smtm		COND_UNLOCK(*cond);
232129484Smtm		return (EINVAL);
233129484Smtm	}
234129484Smtm	/* Remember the mutex */
235129484Smtm	(*cond)->c_mutex = *mutex;
236112918Sjeff
237129484Smtm	_thread_enter_cancellation_point();
238129484Smtm	if ((rval = _mutex_cv_unlock(mutex)) != 0) {
239129484Smtm		if (rval == -1){
240129484Smtm			printf("mutex unlock by condvar failed!");
241129484Smtm			fflush(stdout);
242129484Smtm			abort();
243112918Sjeff		}
244129484Smtm		_thread_leave_cancellation_point();
245129484Smtm		COND_UNLOCK(*cond);
246129484Smtm		return (rval);
247129484Smtm	}
248112918Sjeff
249129484Smtm	/*
250129484Smtm	 * We need to protect the queue operations.  It also
251129484Smtm	 * protects the pthread flag field.  This is
252129484Smtm	 * dropped before calling _thread_suspend() and reaquired
253129484Smtm	 * when we return.
254129484Smtm	 */
255129484Smtm	PTHREAD_LOCK(curthread);
256112918Sjeff
257129484Smtm	/*
258129484Smtm	 * Queue the running thread on the condition
259129484Smtm	 * variable and wait to be signaled.
260129484Smtm	 */
261129484Smtm	cond_queue_enq(*cond, curthread);
262129484Smtm	do {
263129484Smtm		PTHREAD_UNLOCK(curthread);
264129484Smtm		COND_UNLOCK(*cond);
265129484Smtm		if (curthread->cancellation == CS_PENDING) {
266112918Sjeff			/*
267129484Smtm			 * Posix says we must lock the mutex
268129484Smtm			 * even if we're being canceled.
269112918Sjeff			 */
270129484Smtm			_mutex_cv_lock(mutex);
271129484Smtm			_thread_leave_cancellation_point();
272129484Smtm			PANIC("Shouldn't have come back.");
273129484Smtm		}
274129484Smtm		rval = _thread_suspend(curthread, (struct timespec *)abstime);
275129484Smtm		if (rval != 0 && rval != ETIMEDOUT && rval != EINTR) {
276129484Smtm			printf("thread suspend returned an invalid value");
277129484Smtm			fflush(stdout);
278129484Smtm			abort();
279129484Smtm		}
280129484Smtm		COND_LOCK(*cond);
281129484Smtm		PTHREAD_LOCK(curthread);
282129484Smtm		if (rval == ETIMEDOUT) {
283115277Smtm			/*
284129484Smtm			 * Condition may have been signaled between the
285129484Smtm			 * time the thread timed out and locked the condvar.
286129484Smtm			 * If it wasn't, manually remove it from the queue.
287115277Smtm			 */
288129484Smtm			if ((curthread->flags & PTHREAD_FLAGS_IN_CONDQ) == 0)
289112918Sjeff				rval = 0;
290129484Smtm			else
291129484Smtm				cond_queue_remove(*cond, curthread);
292112918Sjeff		}
293129484Smtm	} while ((curthread->flags & PTHREAD_FLAGS_IN_CONDQ) != 0);
294112918Sjeff
295129484Smtm	PTHREAD_UNLOCK(curthread);
296129484Smtm	COND_UNLOCK(*cond);
297129484Smtm	mtxrval = _mutex_cv_lock(mutex);
298112918Sjeff
299129484Smtm	/* If the mutex failed return that error. */
300129484Smtm	if (mtxrval == -1) {
301129484Smtm		printf("mutex lock from condvar failed!");
302129484Smtm		fflush(stdout);
303129484Smtm		abort();
304112918Sjeff	}
305129484Smtm	if (mtxrval != 0)
306129484Smtm		rval = mtxrval;
307112918Sjeff
308112918Sjeff	_thread_leave_cancellation_point();
309112918Sjeff	return (rval);
310112918Sjeff}
311112918Sjeff
312112918Sjeffint
313112918Sjeff_pthread_cond_signal(pthread_cond_t * cond)
314112918Sjeff{
315115277Smtm	return (cond_signal(cond, 0));
316112918Sjeff}
317112918Sjeff
318112918Sjeffint
319112918Sjeff_pthread_cond_broadcast(pthread_cond_t * cond)
320112918Sjeff{
321115277Smtm	return (cond_signal(cond, 1));
322115277Smtm}
323115277Smtm
324115277Smtmstatic int
325115277Smtmcond_signal(pthread_cond_t * cond, int broadcast)
326115277Smtm{
327112918Sjeff	int             rval = 0;
328112918Sjeff	pthread_t       pthread;
329112918Sjeff
330112918Sjeff	if (cond == NULL)
331112918Sjeff		return (EINVAL);
332112918Sjeff       /*
333112918Sjeff        * If the condition variable is statically initialized, perform dynamic
334112918Sjeff        * initialization.
335112918Sjeff        */
336115389Smtm	if (*cond == PTHREAD_COND_INITIALIZER && (rval = cond_init(cond)) != 0)
337112918Sjeff		return (rval);
338112918Sjeff
339129484Smtm	if ((*cond)->c_type != COND_TYPE_FAST)
340129484Smtm		return (EINVAL);
341112918Sjeff	COND_LOCK(*cond);
342112918Sjeff
343129484Smtm	/*
344129484Smtm	 * Enter a loop to bring all (or only one) threads off the
345129484Smtm	 * condition queue:
346129484Smtm	 */
347129484Smtm	do {
348112918Sjeff		/*
349129484Smtm		 * Wake up the signaled thread. It will be returned
350129484Smtm		 * to us locked.
351112918Sjeff		 */
352129484Smtm		if ((pthread = cond_queue_deq(*cond)) != NULL) {
353129484Smtm			PTHREAD_WAKE(pthread);
354129484Smtm			PTHREAD_UNLOCK(pthread);
355129484Smtm		}
356129484Smtm	} while (broadcast && pthread != NULL);
357112918Sjeff
358112918Sjeff	COND_UNLOCK(*cond);
359112918Sjeff	return (rval);
360112918Sjeff}
361112918Sjeff
362112918Sjeffvoid
363112918Sjeff_cond_wait_backout(pthread_t pthread)
364112918Sjeff{
365112918Sjeff	pthread_cond_t	cond;
366112918Sjeff
367112918Sjeff	cond = pthread->data.cond;
368112918Sjeff	if (cond == NULL)
369112918Sjeff		return;
370112918Sjeff
371112918Sjeff	/* Process according to condition variable type: */
372112918Sjeff	switch (cond->c_type) {
373112918Sjeff	/* Fast condition variable: */
374112918Sjeff	case COND_TYPE_FAST:
375112918Sjeff		cond_queue_remove(cond, pthread);
376112918Sjeff		break;
377112918Sjeff	default:
378112918Sjeff		break;
379112918Sjeff	}
380112918Sjeff}
381112918Sjeff
382112918Sjeff/*
383112918Sjeff * Dequeue a waiting thread from the head of a condition queue in
384112918Sjeff * descending priority order.
385112918Sjeff */
386112918Sjeffstatic pthread_t
387112918Sjeffcond_queue_deq(pthread_cond_t cond)
388112918Sjeff{
389112918Sjeff	pthread_t pthread;
390112918Sjeff
391112918Sjeff	while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
392129484Smtm		PTHREAD_LOCK(pthread);
393112918Sjeff		cond_queue_remove(cond, pthread);
394129484Smtm
395129484Smtm		/*
396129484Smtm		 * Only exit the loop when we find a thread
397129484Smtm		 * that hasn't been canceled.
398129484Smtm		 */
399129484Smtm		if (pthread->cancellation == CS_NULL)
400112918Sjeff			break;
401115277Smtm		else
402129484Smtm			PTHREAD_UNLOCK(pthread);
403112918Sjeff	}
404112918Sjeff
405112918Sjeff	return(pthread);
406112918Sjeff}
407112918Sjeff
408112918Sjeff/*
409112918Sjeff * Remove a waiting thread from a condition queue in descending priority
410112918Sjeff * order.
411112918Sjeff */
412112918Sjeffstatic void
413112918Sjeffcond_queue_remove(pthread_cond_t cond, pthread_t pthread)
414112918Sjeff{
415112918Sjeff	/*
416112918Sjeff	 * Because pthread_cond_timedwait() can timeout as well
417112918Sjeff	 * as be signaled by another thread, it is necessary to
418112918Sjeff	 * guard against removing the thread from the queue if
419112918Sjeff	 * it isn't in the queue.
420112918Sjeff	 */
421112918Sjeff	if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) {
422112918Sjeff		TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
423112918Sjeff		pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
424112918Sjeff	}
425112918Sjeff	/* Check for no more waiters. */
426112918Sjeff	if (TAILQ_FIRST(&cond->c_queue) == NULL)
427112918Sjeff		cond->c_mutex = NULL;
428112918Sjeff}
429112918Sjeff
430112918Sjeff/*
431112918Sjeff * Enqueue a waiting thread to a condition queue in descending priority
432112918Sjeff * order.
433112918Sjeff */
434112918Sjeffstatic void
435112918Sjeffcond_queue_enq(pthread_cond_t cond, pthread_t pthread)
436112918Sjeff{
437112918Sjeff	pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
438115198Smtm	char *name;
439112918Sjeff
440115198Smtm	name = pthread->name ? pthread->name : "unknown";
441115198Smtm	if ((pthread->flags & PTHREAD_FLAGS_IN_CONDQ) != 0)
442115198Smtm		_thread_printf(2, "Thread (%s:%u) already on condq\n",
443115198Smtm		    pthread->name, pthread->uniqueid);
444115198Smtm	if ((pthread->flags & PTHREAD_FLAGS_IN_MUTEXQ) != 0)
445115198Smtm		_thread_printf(2, "Thread (%s:%u) already on mutexq\n",
446115198Smtm		    pthread->name, pthread->uniqueid);
447112918Sjeff	PTHREAD_ASSERT_NOT_IN_SYNCQ(pthread);
448112918Sjeff
449112918Sjeff	/*
450112918Sjeff	 * For the common case of all threads having equal priority,
451112918Sjeff	 * we perform a quick check against the priority of the thread
452112918Sjeff	 * at the tail of the queue.
453112918Sjeff	 */
454112918Sjeff	if ((tid == NULL) || (pthread->active_priority <= tid->active_priority))
455112918Sjeff		TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe);
456112918Sjeff	else {
457112918Sjeff		tid = TAILQ_FIRST(&cond->c_queue);
458112918Sjeff		while (pthread->active_priority <= tid->active_priority)
459112918Sjeff			tid = TAILQ_NEXT(tid, sqe);
460112918Sjeff		TAILQ_INSERT_BEFORE(tid, pthread, sqe);
461112918Sjeff	}
462112918Sjeff	pthread->flags |= PTHREAD_FLAGS_IN_CONDQ;
463112918Sjeff	pthread->data.cond = cond;
464112918Sjeff}
465115389Smtm
466115389Smtmstatic inline int
467115389Smtmcond_init(pthread_cond_t *cond)
468115389Smtm{
469115442Smtm	int error = 0;
470115389Smtm	_SPINLOCK(&static_cond_lock);
471115389Smtm	if (*cond == PTHREAD_COND_INITIALIZER)
472115442Smtm		error = _pthread_cond_init(cond, NULL);
473115389Smtm	_SPINUNLOCK(&static_cond_lock);
474115442Smtm	return (error);
475115389Smtm}
476115389Smtm
477