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