thr_cond.c revision 129484
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/libthr/thread/thr_cond.c 129484 2004-05-20 12:06:16Z mtm $
33 */
34#include <stdlib.h>
35#include <errno.h>
36#include <string.h>
37#include <pthread.h>
38#include "thr_private.h"
39
40/*
41 * Proctect two different threads calling a pthread_cond_* function
42 * from accidentally initializing the condition variable twice.
43 */
44static spinlock_t static_cond_lock = _SPINLOCK_INITIALIZER;
45
46/*
47 * Prototypes
48 */
49static inline int	cond_init(pthread_cond_t *);
50static pthread_t	cond_queue_deq(pthread_cond_t);
51static void		cond_queue_remove(pthread_cond_t, pthread_t);
52static void		cond_queue_enq(pthread_cond_t, pthread_t);
53static int		cond_signal(pthread_cond_t *, int);
54static int		cond_wait_common(pthread_cond_t *,
55			    pthread_mutex_t *, const struct timespec *);
56
57__weak_reference(_pthread_cond_init, pthread_cond_init);
58__weak_reference(_pthread_cond_destroy, pthread_cond_destroy);
59__weak_reference(_pthread_cond_wait, pthread_cond_wait);
60__weak_reference(_pthread_cond_timedwait, pthread_cond_timedwait);
61__weak_reference(_pthread_cond_signal, pthread_cond_signal);
62__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
63
64#define	COND_LOCK(c)						\
65do {								\
66	if (umtx_lock(&(c)->c_lock, curthread->thr_id))		\
67		abort();					\
68} while (0)
69
70#define	COND_UNLOCK(c)						\
71do {								\
72	if (umtx_unlock(&(c)->c_lock, curthread->thr_id))	\
73		abort();					\
74} while (0)
75
76
77/* Reinitialize a condition variable to defaults. */
78int
79_cond_reinit(pthread_cond_t *cond)
80{
81	if (cond == NULL)
82		return (EINVAL);
83
84	if (*cond == NULL)
85		return (pthread_cond_init(cond, NULL));
86
87	/*
88	 * Initialize the condition variable structure:
89	 */
90	TAILQ_INIT(&(*cond)->c_queue);
91	(*cond)->c_flags = COND_FLAGS_INITED;
92	(*cond)->c_type = COND_TYPE_FAST;
93	(*cond)->c_mutex = NULL;
94	(*cond)->c_seqno = 0;
95	bzero(&(*cond)->c_lock, sizeof((*cond)->c_lock));
96
97	return (0);
98}
99
100int
101_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
102{
103	enum pthread_cond_type type;
104	pthread_cond_t	pcond;
105
106	if (cond == NULL)
107		return (EINVAL);
108
109	/*
110	 * Check if a pointer to a condition variable attribute
111	 * structure was passed by the caller:
112	 */
113	if (cond_attr != NULL && *cond_attr != NULL)
114		type = (*cond_attr)->c_type;
115	else
116		/* Default to a fast condition variable: */
117		type = COND_TYPE_FAST;
118
119	/* Process according to condition variable type: */
120	switch (type) {
121	case COND_TYPE_FAST:
122		break;
123	default:
124		return (EINVAL);
125		break;
126	}
127
128	if ((pcond = (pthread_cond_t)
129	    malloc(sizeof(struct pthread_cond))) == NULL)
130		return (ENOMEM);
131	/*
132	 * Initialise the condition variable
133	 * structure:
134	 */
135	TAILQ_INIT(&pcond->c_queue);
136	pcond->c_flags |= COND_FLAGS_INITED;
137	pcond->c_type = type;
138	pcond->c_mutex = NULL;
139	pcond->c_seqno = 0;
140	bzero(&pcond->c_lock, sizeof(pcond->c_lock));
141
142	*cond = pcond;
143
144	return (0);
145}
146
147int
148_pthread_cond_destroy(pthread_cond_t *cond)
149{
150	/*
151	 * Short circuit for a statically initialized condvar
152	 * that is being destroyed without having been used.
153	 */
154	if (*cond == PTHREAD_COND_INITIALIZER)
155		return (0);
156
157	COND_LOCK(*cond);
158
159	/*
160	 * Free the memory allocated for the condition
161	 * variable structure:
162	 */
163	free(*cond);
164
165	/*
166	 * NULL the caller's pointer now that the condition
167	 * variable has been destroyed:
168	 */
169	*cond = NULL;
170
171	return (0);
172}
173
174int
175_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
176{
177	int rval;
178
179	rval = cond_wait_common(cond, mutex, NULL);
180
181	/* This should never happen. */
182	if (rval == ETIMEDOUT)
183		abort();
184
185	return (rval);
186}
187
188int
189_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
190		       const struct timespec * abstime)
191{
192	if (abstime == NULL || abstime->tv_nsec >= 1000000000)
193		return (EINVAL);
194
195	return (cond_wait_common(cond, mutex, abstime));
196}
197
198static int
199cond_wait_common(pthread_cond_t * cond, pthread_mutex_t * mutex,
200	         const struct timespec * abstime)
201{
202	int	rval = 0;
203	int	mtxrval;
204
205
206	if (cond == NULL)
207		return (EINVAL);
208	/*
209	 * If the condition variable is statically initialized, perform dynamic
210	 * initialization.
211	 */
212	if (*cond == PTHREAD_COND_INITIALIZER && (rval = cond_init(cond)) != 0)
213		return (rval);
214
215	if ((*cond)->c_type != COND_TYPE_FAST)
216		return (EINVAL);
217
218	COND_LOCK(*cond);
219
220	/*
221	 * If the condvar was statically allocated, properly
222	 * initialize the tail queue.
223	 */
224	if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
225		TAILQ_INIT(&(*cond)->c_queue);
226		(*cond)->c_flags |= COND_FLAGS_INITED;
227	}
228
229	if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
230	    ((*cond)->c_mutex != *mutex))) {
231		COND_UNLOCK(*cond);
232		return (EINVAL);
233	}
234	/* Remember the mutex */
235	(*cond)->c_mutex = *mutex;
236
237	_thread_enter_cancellation_point();
238	if ((rval = _mutex_cv_unlock(mutex)) != 0) {
239		if (rval == -1){
240			printf("mutex unlock by condvar failed!");
241			fflush(stdout);
242			abort();
243		}
244		_thread_leave_cancellation_point();
245		COND_UNLOCK(*cond);
246		return (rval);
247	}
248
249	/*
250	 * We need to protect the queue operations.  It also
251	 * protects the pthread flag field.  This is
252	 * dropped before calling _thread_suspend() and reaquired
253	 * when we return.
254	 */
255	PTHREAD_LOCK(curthread);
256
257	/*
258	 * Queue the running thread on the condition
259	 * variable and wait to be signaled.
260	 */
261	cond_queue_enq(*cond, curthread);
262	do {
263		PTHREAD_UNLOCK(curthread);
264		COND_UNLOCK(*cond);
265		if (curthread->cancellation == CS_PENDING) {
266			/*
267			 * Posix says we must lock the mutex
268			 * even if we're being canceled.
269			 */
270			_mutex_cv_lock(mutex);
271			_thread_leave_cancellation_point();
272			PANIC("Shouldn't have come back.");
273		}
274		rval = _thread_suspend(curthread, (struct timespec *)abstime);
275		if (rval != 0 && rval != ETIMEDOUT && rval != EINTR) {
276			printf("thread suspend returned an invalid value");
277			fflush(stdout);
278			abort();
279		}
280		COND_LOCK(*cond);
281		PTHREAD_LOCK(curthread);
282		if (rval == ETIMEDOUT) {
283			/*
284			 * Condition may have been signaled between the
285			 * time the thread timed out and locked the condvar.
286			 * If it wasn't, manually remove it from the queue.
287			 */
288			if ((curthread->flags & PTHREAD_FLAGS_IN_CONDQ) == 0)
289				rval = 0;
290			else
291				cond_queue_remove(*cond, curthread);
292		}
293	} while ((curthread->flags & PTHREAD_FLAGS_IN_CONDQ) != 0);
294
295	PTHREAD_UNLOCK(curthread);
296	COND_UNLOCK(*cond);
297	mtxrval = _mutex_cv_lock(mutex);
298
299	/* If the mutex failed return that error. */
300	if (mtxrval == -1) {
301		printf("mutex lock from condvar failed!");
302		fflush(stdout);
303		abort();
304	}
305	if (mtxrval != 0)
306		rval = mtxrval;
307
308	_thread_leave_cancellation_point();
309	return (rval);
310}
311
312int
313_pthread_cond_signal(pthread_cond_t * cond)
314{
315	return (cond_signal(cond, 0));
316}
317
318int
319_pthread_cond_broadcast(pthread_cond_t * cond)
320{
321	return (cond_signal(cond, 1));
322}
323
324static int
325cond_signal(pthread_cond_t * cond, int broadcast)
326{
327	int             rval = 0;
328	pthread_t       pthread;
329
330	if (cond == NULL)
331		return (EINVAL);
332       /*
333        * If the condition variable is statically initialized, perform dynamic
334        * initialization.
335        */
336	if (*cond == PTHREAD_COND_INITIALIZER && (rval = cond_init(cond)) != 0)
337		return (rval);
338
339	if ((*cond)->c_type != COND_TYPE_FAST)
340		return (EINVAL);
341	COND_LOCK(*cond);
342
343	/*
344	 * Enter a loop to bring all (or only one) threads off the
345	 * condition queue:
346	 */
347	do {
348		/*
349		 * Wake up the signaled thread. It will be returned
350		 * to us locked.
351		 */
352		if ((pthread = cond_queue_deq(*cond)) != NULL) {
353			PTHREAD_WAKE(pthread);
354			PTHREAD_UNLOCK(pthread);
355		}
356	} while (broadcast && pthread != NULL);
357
358	COND_UNLOCK(*cond);
359	return (rval);
360}
361
362void
363_cond_wait_backout(pthread_t pthread)
364{
365	pthread_cond_t	cond;
366
367	cond = pthread->data.cond;
368	if (cond == NULL)
369		return;
370
371	/* Process according to condition variable type: */
372	switch (cond->c_type) {
373	/* Fast condition variable: */
374	case COND_TYPE_FAST:
375		cond_queue_remove(cond, pthread);
376		break;
377	default:
378		break;
379	}
380}
381
382/*
383 * Dequeue a waiting thread from the head of a condition queue in
384 * descending priority order.
385 */
386static pthread_t
387cond_queue_deq(pthread_cond_t cond)
388{
389	pthread_t pthread;
390
391	while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
392		PTHREAD_LOCK(pthread);
393		cond_queue_remove(cond, pthread);
394
395		/*
396		 * Only exit the loop when we find a thread
397		 * that hasn't been canceled.
398		 */
399		if (pthread->cancellation == CS_NULL)
400			break;
401		else
402			PTHREAD_UNLOCK(pthread);
403	}
404
405	return(pthread);
406}
407
408/*
409 * Remove a waiting thread from a condition queue in descending priority
410 * order.
411 */
412static void
413cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
414{
415	/*
416	 * Because pthread_cond_timedwait() can timeout as well
417	 * as be signaled by another thread, it is necessary to
418	 * guard against removing the thread from the queue if
419	 * it isn't in the queue.
420	 */
421	if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) {
422		TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
423		pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
424	}
425	/* Check for no more waiters. */
426	if (TAILQ_FIRST(&cond->c_queue) == NULL)
427		cond->c_mutex = NULL;
428}
429
430/*
431 * Enqueue a waiting thread to a condition queue in descending priority
432 * order.
433 */
434static void
435cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
436{
437	pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
438	char *name;
439
440	name = pthread->name ? pthread->name : "unknown";
441	if ((pthread->flags & PTHREAD_FLAGS_IN_CONDQ) != 0)
442		_thread_printf(2, "Thread (%s:%u) already on condq\n",
443		    pthread->name, pthread->uniqueid);
444	if ((pthread->flags & PTHREAD_FLAGS_IN_MUTEXQ) != 0)
445		_thread_printf(2, "Thread (%s:%u) already on mutexq\n",
446		    pthread->name, pthread->uniqueid);
447	PTHREAD_ASSERT_NOT_IN_SYNCQ(pthread);
448
449	/*
450	 * For the common case of all threads having equal priority,
451	 * we perform a quick check against the priority of the thread
452	 * at the tail of the queue.
453	 */
454	if ((tid == NULL) || (pthread->active_priority <= tid->active_priority))
455		TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe);
456	else {
457		tid = TAILQ_FIRST(&cond->c_queue);
458		while (pthread->active_priority <= tid->active_priority)
459			tid = TAILQ_NEXT(tid, sqe);
460		TAILQ_INSERT_BEFORE(tid, pthread, sqe);
461	}
462	pthread->flags |= PTHREAD_FLAGS_IN_CONDQ;
463	pthread->data.cond = cond;
464}
465
466static inline int
467cond_init(pthread_cond_t *cond)
468{
469	int error = 0;
470	_SPINLOCK(&static_cond_lock);
471	if (*cond == PTHREAD_COND_INITIALIZER)
472		error = _pthread_cond_init(cond, NULL);
473	_SPINUNLOCK(&static_cond_lock);
474	return (error);
475}
476
477