thr_cond.c revision 115035
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 115035 2003-05-15 18:17:13Z 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 * Prototypes
42 */
43static pthread_t	cond_queue_deq(pthread_cond_t);
44static void		cond_queue_remove(pthread_cond_t, pthread_t);
45static void		cond_queue_enq(pthread_cond_t, pthread_t);
46static int		cond_wait_common(pthread_cond_t *,
47			    pthread_mutex_t *, const struct timespec *);
48
49__weak_reference(_pthread_cond_init, pthread_cond_init);
50__weak_reference(_pthread_cond_destroy, pthread_cond_destroy);
51__weak_reference(_pthread_cond_wait, pthread_cond_wait);
52__weak_reference(_pthread_cond_timedwait, pthread_cond_timedwait);
53__weak_reference(_pthread_cond_signal, pthread_cond_signal);
54__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
55
56#define	COND_LOCK(c)						\
57do {								\
58	if (umtx_lock(&(c)->c_lock, curthread->thr_id))		\
59		abort();					\
60} while (0)
61
62#define	COND_UNLOCK(c)						\
63do {								\
64	if (umtx_unlock(&(c)->c_lock, curthread->thr_id))	\
65		abort();					\
66} while (0)
67
68
69/* Reinitialize a condition variable to defaults. */
70int
71_cond_reinit(pthread_cond_t *cond)
72{
73	if (cond == NULL)
74		return (EINVAL);
75
76	if (*cond == NULL)
77		return (pthread_cond_init(cond, NULL));
78
79	/*
80	 * Initialize the condition variable structure:
81	 */
82	TAILQ_INIT(&(*cond)->c_queue);
83	(*cond)->c_flags = COND_FLAGS_INITED;
84	(*cond)->c_type = COND_TYPE_FAST;
85	(*cond)->c_mutex = NULL;
86	(*cond)->c_seqno = 0;
87	bzero(&(*cond)->c_lock, sizeof((*cond)->c_lock));
88
89	return (0);
90}
91
92int
93_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
94{
95	enum pthread_cond_type type;
96	pthread_cond_t	pcond;
97
98	if (cond == NULL)
99		return (EINVAL);
100
101	/*
102	 * Check if a pointer to a condition variable attribute
103	 * structure was passed by the caller:
104	 */
105	if (cond_attr != NULL && *cond_attr != NULL)
106		type = (*cond_attr)->c_type;
107	else
108		/* Default to a fast condition variable: */
109		type = COND_TYPE_FAST;
110
111	/* Process according to condition variable type: */
112	switch (type) {
113	case COND_TYPE_FAST:
114		break;
115	default:
116		return (EINVAL);
117		break;
118	}
119
120	if ((pcond = (pthread_cond_t)
121	    malloc(sizeof(struct pthread_cond))) == NULL)
122		return (ENOMEM);
123	/*
124	 * Initialise the condition variable
125	 * structure:
126	 */
127	TAILQ_INIT(&pcond->c_queue);
128	pcond->c_flags |= COND_FLAGS_INITED;
129	pcond->c_type = type;
130	pcond->c_mutex = NULL;
131	pcond->c_seqno = 0;
132	bzero(&pcond->c_lock, sizeof(pcond->c_lock));
133
134	*cond = pcond;
135
136	return (0);
137}
138
139int
140_pthread_cond_destroy(pthread_cond_t *cond)
141{
142	if (cond == NULL || *cond == NULL)
143		return (EINVAL);
144
145	COND_LOCK(*cond);
146
147	/*
148	 * Free the memory allocated for the condition
149	 * variable structure:
150	 */
151	free(*cond);
152
153	/*
154	 * NULL the caller's pointer now that the condition
155	 * variable has been destroyed:
156	 */
157	*cond = NULL;
158
159	return (0);
160}
161
162int
163_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
164{
165	int rval;
166
167	rval = cond_wait_common(cond, mutex, NULL);
168
169	/* This should never happen. */
170	if (rval == ETIMEDOUT)
171		abort();
172
173	return (rval);
174}
175
176int
177_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
178		       const struct timespec * abstime)
179{
180	if (abstime == NULL || abstime->tv_nsec >= 1000000000)
181		return (EINVAL);
182
183	return (cond_wait_common(cond, mutex, abstime));
184}
185
186static int
187cond_wait_common(pthread_cond_t * cond, pthread_mutex_t * mutex,
188	         const struct timespec * abstime)
189{
190	int	rval = 0;
191	int	done = 0;
192	int	seqno;
193	int	mtxrval;
194
195
196	_thread_enter_cancellation_point();
197
198	/*
199	 * If the condition variable is statically initialized, perform dynamic
200	 * initialization.
201	 */
202	if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0)
203		return (rval);
204
205
206	COND_LOCK(*cond);
207
208	/*
209	 * If the condvar was statically allocated, properly
210	 * initialize the tail queue.
211	 */
212	if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
213		TAILQ_INIT(&(*cond)->c_queue);
214		(*cond)->c_flags |= COND_FLAGS_INITED;
215	}
216
217	/* Process according to condition variable type. */
218
219	switch ((*cond)->c_type) {
220	/* Fast condition variable: */
221	case COND_TYPE_FAST:
222		if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
223		    ((*cond)->c_mutex != *mutex))) {
224			COND_UNLOCK(*cond);
225			rval = EINVAL;
226			break;
227		}
228		/* Remember the mutex */
229		(*cond)->c_mutex = *mutex;
230
231		if ((rval = _mutex_cv_unlock(mutex)) != 0) {
232			if (rval == -1){
233				printf("foo");
234				fflush(stdout);
235				abort();
236			}
237
238			COND_UNLOCK(*cond);
239			break;
240		}
241		COND_UNLOCK(*cond);
242
243		/*
244		 * We need giant for the queue operations.  It also
245		 * protects seqno and the pthread flag fields.  This is
246		 * dropped and reacquired in _thread_suspend().
247		 */
248
249		GIANT_LOCK(curthread);
250		/*
251		 * c_seqno is protected by giant.
252		 */
253		seqno = (*cond)->c_seqno;
254
255		do {
256			/*
257			 * Queue the running thread on the condition
258			 * variable.
259			 */
260			cond_queue_enq(*cond, curthread);
261
262			if (curthread->cancelflags & PTHREAD_CANCELLING) {
263				/*
264				 * POSIX Says that we must relock the mutex
265				 * even if we're being canceled.
266				 */
267				GIANT_UNLOCK(curthread);
268				_mutex_cv_lock(mutex);
269				pthread_testcancel();
270				PANIC("Shouldn't have come back.");
271			}
272
273			PTHREAD_SET_STATE(curthread, PS_COND_WAIT);
274			GIANT_UNLOCK(curthread);
275			rval = _thread_suspend(curthread, (struct timespec *)abstime);
276			if (rval == -1) {
277				printf("foo");
278				fflush(stdout);
279				abort();
280			}
281			GIANT_LOCK(curthread);
282
283			done = (seqno != (*cond)->c_seqno);
284
285			cond_queue_remove(*cond, curthread);
286
287		} while ((done == 0) && (rval == 0));
288		/*
289		 * If we timed out someone still may have signaled us
290		 * before we got a chance to run again.  We check for
291		 * this by looking to see if our state is RUNNING.
292		 */
293		if (rval == EAGAIN) {
294			if (curthread->state != PS_RUNNING) {
295				PTHREAD_SET_STATE(curthread, PS_RUNNING);
296				rval = ETIMEDOUT;
297			} else
298				rval = 0;
299		}
300		GIANT_UNLOCK(curthread);
301
302		mtxrval = _mutex_cv_lock(mutex);
303
304		/*
305		 * If the mutex failed return that error, otherwise we're
306		 * returning ETIMEDOUT.
307		 */
308		if (mtxrval == -1) {
309			printf("foo");
310			fflush(stdout);
311			abort();
312		}
313		if (mtxrval != 0)
314			rval = mtxrval;
315
316		break;
317
318	/* Trap invalid condition variable types: */
319	default:
320		COND_UNLOCK(*cond);
321		rval = EINVAL;
322		break;
323	}
324
325	/*
326	 * See if we have to cancel before we retry.  We could be
327	 * canceled with the mutex held here!
328	 */
329	pthread_testcancel();
330
331	_thread_leave_cancellation_point();
332
333	return (rval);
334}
335
336int
337_pthread_cond_signal(pthread_cond_t * cond)
338{
339	int             rval = 0;
340	pthread_t       pthread;
341
342	if (cond == NULL)
343		return (EINVAL);
344       /*
345        * If the condition variable is statically initialized, perform dynamic
346        * initialization.
347        */
348	if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0)
349		return (rval);
350
351
352	COND_LOCK(*cond);
353
354	/* Process according to condition variable type: */
355	switch ((*cond)->c_type) {
356	/* Fast condition variable: */
357	case COND_TYPE_FAST:
358		GIANT_LOCK(curthread);
359		(*cond)->c_seqno++;
360
361		if ((pthread = cond_queue_deq(*cond)) != NULL) {
362			/*
363			 * Wake up the signaled thread:
364			 */
365			PTHREAD_NEW_STATE(pthread, PS_RUNNING);
366		}
367
368		GIANT_UNLOCK(curthread);
369		break;
370
371	/* Trap invalid condition variable types: */
372	default:
373		rval = EINVAL;
374		break;
375	}
376
377
378	COND_UNLOCK(*cond);
379
380	return (rval);
381}
382
383int
384_pthread_cond_broadcast(pthread_cond_t * cond)
385{
386	int             rval = 0;
387	pthread_t       pthread;
388
389	if (cond == NULL)
390		return (EINVAL);
391       /*
392        * If the condition variable is statically initialized, perform dynamic
393        * initialization.
394        */
395	if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0)
396		return (rval);
397
398	COND_LOCK(*cond);
399
400	/* Process according to condition variable type: */
401	switch ((*cond)->c_type) {
402	/* Fast condition variable: */
403	case COND_TYPE_FAST:
404		GIANT_LOCK(curthread);
405		(*cond)->c_seqno++;
406
407		/*
408		 * Enter a loop to bring all threads off the
409		 * condition queue:
410		 */
411		while ((pthread = cond_queue_deq(*cond)) != NULL) {
412			/*
413			 * Wake up the signaled thread:
414			 */
415			PTHREAD_NEW_STATE(pthread, PS_RUNNING);
416		}
417		GIANT_UNLOCK(curthread);
418
419		/* There are no more waiting threads: */
420		(*cond)->c_mutex = NULL;
421
422		break;
423
424	/* Trap invalid condition variable types: */
425	default:
426		rval = EINVAL;
427		break;
428	}
429
430	COND_UNLOCK(*cond);
431
432
433	return (rval);
434}
435
436void
437_cond_wait_backout(pthread_t pthread)
438{
439	pthread_cond_t	cond;
440
441	cond = pthread->data.cond;
442	if (cond == NULL)
443		return;
444
445	COND_LOCK(cond);
446
447	/* Process according to condition variable type: */
448	switch (cond->c_type) {
449	/* Fast condition variable: */
450	case COND_TYPE_FAST:
451		GIANT_LOCK(curthread);
452
453		cond_queue_remove(cond, pthread);
454
455		GIANT_UNLOCK(curthread);
456		break;
457
458	default:
459		break;
460	}
461
462	COND_UNLOCK(cond);
463}
464
465/*
466 * Dequeue a waiting thread from the head of a condition queue in
467 * descending priority order.
468 */
469static pthread_t
470cond_queue_deq(pthread_cond_t cond)
471{
472	pthread_t pthread;
473
474	while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
475		TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
476		cond_queue_remove(cond, pthread);
477		if ((pthread->cancelflags & PTHREAD_CANCELLING) == 0 &&
478		    pthread->state == PS_COND_WAIT)
479			/*
480			 * Only exit the loop when we find a thread
481			 * that hasn't timed out or been canceled;
482			 * those threads are already running and don't
483			 * need their run state changed.
484			 */
485			break;
486	}
487
488	return(pthread);
489}
490
491/*
492 * Remove a waiting thread from a condition queue in descending priority
493 * order.
494 */
495static void
496cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
497{
498	/*
499	 * Because pthread_cond_timedwait() can timeout as well
500	 * as be signaled by another thread, it is necessary to
501	 * guard against removing the thread from the queue if
502	 * it isn't in the queue.
503	 */
504	if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) {
505		TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
506		pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
507	}
508	/* Check for no more waiters. */
509	if (TAILQ_FIRST(&cond->c_queue) == NULL)
510		cond->c_mutex = NULL;
511}
512
513/*
514 * Enqueue a waiting thread to a condition queue in descending priority
515 * order.
516 */
517static void
518cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
519{
520	pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
521
522	PTHREAD_ASSERT_NOT_IN_SYNCQ(pthread);
523
524	/*
525	 * For the common case of all threads having equal priority,
526	 * we perform a quick check against the priority of the thread
527	 * at the tail of the queue.
528	 */
529	if ((tid == NULL) || (pthread->active_priority <= tid->active_priority))
530		TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe);
531	else {
532		tid = TAILQ_FIRST(&cond->c_queue);
533		while (pthread->active_priority <= tid->active_priority)
534			tid = TAILQ_NEXT(tid, sqe);
535		TAILQ_INSERT_BEFORE(tid, pthread, sqe);
536	}
537	pthread->flags |= PTHREAD_FLAGS_IN_CONDQ;
538	pthread->data.cond = cond;
539}
540