xref: /freebsd/lib/libthr/thread/thr_cond.c (revision 2357939bc239bd5334a169b62313806178dd8f30)
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  * Proctect two different threads calling a pthread_cond_* function
42  * from accidentally initializing the condition variable twice.
43  */
44 static spinlock_t static_cond_lock = _SPINLOCK_INITIALIZER;
45 
46 /*
47  * Prototypes
48  */
49 static inline int	cond_init(pthread_cond_t *);
50 static pthread_t	cond_queue_deq(pthread_cond_t);
51 static void		cond_queue_remove(pthread_cond_t, pthread_t);
52 static void		cond_queue_enq(pthread_cond_t, pthread_t);
53 static int		cond_signal(pthread_cond_t *, int);
54 static 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)						\
65 do {								\
66 	if (umtx_lock(&(c)->c_lock, curthread->thr_id))		\
67 		abort();					\
68 } while (0)
69 
70 #define	COND_UNLOCK(c)						\
71 do {								\
72 	if (umtx_unlock(&(c)->c_lock, curthread->thr_id))	\
73 		abort();					\
74 } while (0)
75 
76 
77 /* Reinitialize a condition variable to defaults. */
78 int
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 
100 int
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 
147 int
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 
174 int
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 
188 int
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 
198 static int
199 cond_wait_common(pthread_cond_t * cond, pthread_mutex_t * mutex,
200 	         const struct timespec * abstime)
201 {
202 	int	rval = 0;
203 	int	done = 0;
204 	int	seqno;
205 	int	mtxrval;
206 
207 
208 	_thread_enter_cancellation_point();
209 
210 	if (cond == NULL)
211 		return (EINVAL);
212 	/*
213 	 * If the condition variable is statically initialized, perform dynamic
214 	 * initialization.
215 	 */
216 	if (*cond == PTHREAD_COND_INITIALIZER && (rval = cond_init(cond)) != 0)
217 		return (rval);
218 
219 
220 	COND_LOCK(*cond);
221 
222 	/*
223 	 * If the condvar was statically allocated, properly
224 	 * initialize the tail queue.
225 	 */
226 	if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
227 		TAILQ_INIT(&(*cond)->c_queue);
228 		(*cond)->c_flags |= COND_FLAGS_INITED;
229 	}
230 
231 	/* Process according to condition variable type. */
232 
233 	switch ((*cond)->c_type) {
234 	/* Fast condition variable: */
235 	case COND_TYPE_FAST:
236 		if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
237 		    ((*cond)->c_mutex != *mutex))) {
238 			COND_UNLOCK(*cond);
239 			rval = EINVAL;
240 			break;
241 		}
242 		/* Remember the mutex */
243 		(*cond)->c_mutex = *mutex;
244 
245 		if ((rval = _mutex_cv_unlock(mutex)) != 0) {
246 			if (rval == -1){
247 				printf("foo");
248 				fflush(stdout);
249 				abort();
250 			}
251 
252 			COND_UNLOCK(*cond);
253 			break;
254 		}
255 
256 		/*
257 		 * We need to protect the queue operations.  It also
258 		 * protects c_seqno and the pthread flag fields.  This is
259 		 * dropped before calling _thread_suspend() and reaquired
260 		 * when we return.
261 		 */
262 
263 		_thread_critical_enter(curthread);
264 		/*
265 		 * c_seqno is protected.
266 		 */
267 		seqno = (*cond)->c_seqno;
268 
269 		do {
270 			/*
271 			 * Queue the running thread on the condition
272 			 * variable.
273 			 */
274 			cond_queue_enq(*cond, curthread);
275 
276 			if (curthread->cancelflags & PTHREAD_CANCELLING) {
277 				/*
278 				 * POSIX Says that we must relock the mutex
279 				 * even if we're being canceled.
280 				 */
281 				_thread_critical_exit(curthread);
282 				COND_UNLOCK(*cond);
283 				_mutex_cv_lock(mutex);
284 				pthread_testcancel();
285 				PANIC("Shouldn't have come back.");
286 			}
287 
288 			PTHREAD_SET_STATE(curthread, PS_COND_WAIT);
289 			_thread_critical_exit(curthread);
290 			COND_UNLOCK(*cond);
291 			rval = _thread_suspend(curthread, (struct timespec *)abstime);
292 			if (rval != 0 && rval != ETIMEDOUT && rval != EINTR) {
293 				printf("foo");
294 				fflush(stdout);
295 				abort();
296 			}
297 			COND_LOCK(*cond);
298 			_thread_critical_enter(curthread);
299 
300 			done = (seqno != (*cond)->c_seqno);
301 
302 			/*
303 			 * If we timed out, this will remove us from the
304 			 * queue. Otherwise, if we were signaled it does
305 			 * nothing because this thread won't be on the queue.
306 			 */
307 			cond_queue_remove(*cond, curthread);
308 
309 		} while ((done == 0) && (rval == 0));
310 		/*
311 		 * If we timed out someone still may have signaled us
312 		 * before we got a chance to run again.  We check for
313 		 * this by looking to see if our state is RUNNING.
314 		 */
315 		if (rval == ETIMEDOUT) {
316 			if (curthread->state != PS_RUNNING) {
317 				PTHREAD_SET_STATE(curthread, PS_RUNNING);
318 			} else
319 				rval = 0;
320 		}
321 		_thread_critical_exit(curthread);
322 		COND_UNLOCK(*cond);
323 
324 		mtxrval = _mutex_cv_lock(mutex);
325 
326 		/*
327 		 * If the mutex failed return that error, otherwise we're
328 		 * returning ETIMEDOUT.
329 		 */
330 		if (mtxrval == -1) {
331 			printf("foo");
332 			fflush(stdout);
333 			abort();
334 		}
335 		if (mtxrval != 0)
336 			rval = mtxrval;
337 
338 		break;
339 
340 	/* Trap invalid condition variable types: */
341 	default:
342 		COND_UNLOCK(*cond);
343 		rval = EINVAL;
344 		break;
345 	}
346 
347 	_thread_leave_cancellation_point();
348 
349 	return (rval);
350 }
351 
352 int
353 _pthread_cond_signal(pthread_cond_t * cond)
354 {
355 	return (cond_signal(cond, 0));
356 }
357 
358 int
359 _pthread_cond_broadcast(pthread_cond_t * cond)
360 {
361 	return (cond_signal(cond, 1));
362 }
363 
364 static int
365 cond_signal(pthread_cond_t * cond, int broadcast)
366 {
367 	int             rval = 0;
368 	pthread_t       pthread;
369 
370 	if (cond == NULL)
371 		return (EINVAL);
372        /*
373         * If the condition variable is statically initialized, perform dynamic
374         * initialization.
375         */
376 	if (*cond == PTHREAD_COND_INITIALIZER && (rval = cond_init(cond)) != 0)
377 		return (rval);
378 
379 	COND_LOCK(*cond);
380 
381 	/* Process according to condition variable type: */
382 	switch ((*cond)->c_type) {
383 	/* Fast condition variable: */
384 	case COND_TYPE_FAST:
385 		(*cond)->c_seqno++;
386 
387 		/*
388 		 * Enter a loop to bring all (or only one) threads off the
389 		 * condition queue:
390 		 */
391 		do {
392 			/*
393 			 * Wake up the signaled thread. It will be returned
394 			 * to us locked, and with signals disabled.
395 			 */
396 			if ((pthread = cond_queue_deq(*cond)) != NULL) {
397 				PTHREAD_NEW_STATE(pthread, PS_RUNNING);
398 				_thread_critical_exit(pthread);
399 			}
400 		} while (broadcast && pthread != NULL);
401 
402 		break;
403 
404 	/* Trap invalid condition variable types: */
405 	default:
406 		rval = EINVAL;
407 		break;
408 	}
409 
410 	COND_UNLOCK(*cond);
411 
412 
413 	return (rval);
414 }
415 
416 void
417 _cond_wait_backout(pthread_t pthread)
418 {
419 	pthread_cond_t	cond;
420 
421 	cond = pthread->data.cond;
422 	if (cond == NULL)
423 		return;
424 
425 	/* Process according to condition variable type: */
426 	switch (cond->c_type) {
427 	/* Fast condition variable: */
428 	case COND_TYPE_FAST:
429 		cond_queue_remove(cond, pthread);
430 		break;
431 	default:
432 		break;
433 	}
434 }
435 
436 /*
437  * Dequeue a waiting thread from the head of a condition queue in
438  * descending priority order.
439  */
440 static pthread_t
441 cond_queue_deq(pthread_cond_t cond)
442 {
443 	pthread_t pthread;
444 
445 	while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
446 		_thread_critical_enter(pthread);
447 		TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
448 		cond_queue_remove(cond, pthread);
449 		if ((pthread->cancelflags & PTHREAD_CANCELLING) == 0 &&
450 		    pthread->state == PS_COND_WAIT)
451 			/*
452 			 * Only exit the loop when we find a thread
453 			 * that hasn't timed out or been canceled;
454 			 * those threads are already running and don't
455 			 * need their run state changed.
456 			 */
457 			break;
458 		else
459 			_thread_critical_exit(pthread);
460 	}
461 
462 	return(pthread);
463 }
464 
465 /*
466  * Remove a waiting thread from a condition queue in descending priority
467  * order.
468  */
469 static void
470 cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
471 {
472 	/*
473 	 * Because pthread_cond_timedwait() can timeout as well
474 	 * as be signaled by another thread, it is necessary to
475 	 * guard against removing the thread from the queue if
476 	 * it isn't in the queue.
477 	 */
478 	if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) {
479 		TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
480 		pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
481 	}
482 	/* Check for no more waiters. */
483 	if (TAILQ_FIRST(&cond->c_queue) == NULL)
484 		cond->c_mutex = NULL;
485 }
486 
487 /*
488  * Enqueue a waiting thread to a condition queue in descending priority
489  * order.
490  */
491 static void
492 cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
493 {
494 	pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
495 	char *name;
496 
497 	name = pthread->name ? pthread->name : "unknown";
498 	if ((pthread->flags & PTHREAD_FLAGS_IN_CONDQ) != 0)
499 		_thread_printf(2, "Thread (%s:%u) already on condq\n",
500 		    pthread->name, pthread->uniqueid);
501 	if ((pthread->flags & PTHREAD_FLAGS_IN_MUTEXQ) != 0)
502 		_thread_printf(2, "Thread (%s:%u) already on mutexq\n",
503 		    pthread->name, pthread->uniqueid);
504 	PTHREAD_ASSERT_NOT_IN_SYNCQ(pthread);
505 
506 	/*
507 	 * For the common case of all threads having equal priority,
508 	 * we perform a quick check against the priority of the thread
509 	 * at the tail of the queue.
510 	 */
511 	if ((tid == NULL) || (pthread->active_priority <= tid->active_priority))
512 		TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe);
513 	else {
514 		tid = TAILQ_FIRST(&cond->c_queue);
515 		while (pthread->active_priority <= tid->active_priority)
516 			tid = TAILQ_NEXT(tid, sqe);
517 		TAILQ_INSERT_BEFORE(tid, pthread, sqe);
518 	}
519 	pthread->flags |= PTHREAD_FLAGS_IN_CONDQ;
520 	pthread->data.cond = cond;
521 }
522 
523 static inline int
524 cond_init(pthread_cond_t *cond)
525 {
526 	int error = 0;
527 	_SPINLOCK(&static_cond_lock);
528 	if (*cond == PTHREAD_COND_INITIALIZER)
529 		error = _pthread_cond_init(cond, NULL);
530 	_SPINUNLOCK(&static_cond_lock);
531 	return (error);
532 }
533 
534