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