xref: /freebsd/lib/libthr/thread/thr_cond.c (revision 6b3455a7665208c366849f0b2b3bc916fb97516e)
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	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 
312 int
313 _pthread_cond_signal(pthread_cond_t * cond)
314 {
315 	return (cond_signal(cond, 0));
316 }
317 
318 int
319 _pthread_cond_broadcast(pthread_cond_t * cond)
320 {
321 	return (cond_signal(cond, 1));
322 }
323 
324 static int
325 cond_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 
362 void
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  */
386 static pthread_t
387 cond_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  */
412 static void
413 cond_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  */
434 static void
435 cond_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:%ld) already on condq\n",
443 		    pthread->name, pthread->thr_id);
444 	if ((pthread->flags & PTHREAD_FLAGS_IN_MUTEXQ) != 0)
445 		_thread_printf(2, "Thread (%s:%ld) already on mutexq\n",
446 		    pthread->name, pthread->thr_id);
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 
466 static inline int
467 cond_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