xref: /netbsd-src/lib/libpthread/pthread_cond.c (revision 7fa608457b817eca6e0977b37f758ae064f3c99c)
1 /*	$NetBSD: pthread_cond.c,v 1.37 2007/09/13 23:51:47 ad Exp $	*/
2 
3 /*-
4  * Copyright (c) 2001, 2006, 2007 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Nathan J. Williams and Andrew Doran.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 __RCSID("$NetBSD: pthread_cond.c,v 1.37 2007/09/13 23:51:47 ad Exp $");
41 
42 #include <errno.h>
43 #include <sys/time.h>
44 #include <sys/types.h>
45 
46 #include "pthread.h"
47 #include "pthread_int.h"
48 
49 int	_sys_nanosleep(const struct timespec *, struct timespec *);
50 
51 extern int pthread__started;
52 
53 static int pthread_cond_wait_nothread(pthread_t, pthread_mutex_t *,
54     const struct timespec *);
55 
56 __strong_alias(__libc_cond_init,pthread_cond_init)
57 __strong_alias(__libc_cond_signal,pthread_cond_signal)
58 __strong_alias(__libc_cond_broadcast,pthread_cond_broadcast)
59 __strong_alias(__libc_cond_wait,pthread_cond_wait)
60 __strong_alias(__libc_cond_timedwait,pthread_cond_timedwait)
61 __strong_alias(__libc_cond_destroy,pthread_cond_destroy)
62 
63 int
64 pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr)
65 {
66 
67 	pthread__error(EINVAL, "Invalid condition variable attribute",
68 	    (attr == NULL) || (attr->ptca_magic == _PT_CONDATTR_MAGIC));
69 
70 	cond->ptc_magic = _PT_COND_MAGIC;
71 	pthread_lockinit(&cond->ptc_lock);
72 	PTQ_INIT(&cond->ptc_waiters);
73 	cond->ptc_mutex = NULL;
74 
75 	return 0;
76 }
77 
78 
79 int
80 pthread_cond_destroy(pthread_cond_t *cond)
81 {
82 
83 	pthread__error(EINVAL, "Invalid condition variable",
84 	    cond->ptc_magic == _PT_COND_MAGIC);
85 	pthread__error(EBUSY, "Destroying condition variable in use",
86 	    cond->ptc_mutex == NULL);
87 
88 	cond->ptc_magic = _PT_COND_DEAD;
89 
90 	return 0;
91 }
92 
93 
94 int
95 pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
96 {
97 	pthread_t self;
98 
99 	pthread__error(EINVAL, "Invalid condition variable",
100 	    cond->ptc_magic == _PT_COND_MAGIC);
101 	pthread__error(EINVAL, "Invalid mutex",
102 	    mutex->ptm_magic == _PT_MUTEX_MAGIC);
103 	pthread__error(EPERM, "Mutex not locked in condition wait",
104 	    mutex->ptm_owner != NULL);
105 
106 	self = pthread__self();
107 	PTHREADD_ADD(PTHREADD_COND_WAIT);
108 
109 	/* Just hang out for a while if threads aren't running yet. */
110 	if (__predict_false(pthread__started == 0))
111 		return pthread_cond_wait_nothread(self, mutex, NULL);
112 
113 	if (__predict_false(self->pt_cancel))
114 		pthread_exit(PTHREAD_CANCELED);
115 
116 	/*
117 	 * Note this thread as waiting on the CV.  To ensure good
118 	 * performance it's critical that the spinlock is held for
119 	 * as short a time as possible - that means no system calls.
120 	 */
121 	pthread_spinlock(&cond->ptc_lock);
122 #ifdef ERRORCHECK
123 	if (cond->ptc_mutex == NULL)
124 		cond->ptc_mutex = mutex;
125 	else {
126 		pthread__error(EINVAL,
127 		    "Multiple mutexes used for condition wait",
128 		    cond->ptc_mutex == mutex);
129 	}
130 #else
131 	cond->ptc_mutex = mutex;
132 #endif
133 	PTQ_INSERT_HEAD(&cond->ptc_waiters, self, pt_sleep);
134 	self->pt_signalled = 0;
135 	self->pt_sleeponq = 1;
136 	self->pt_sleepobj = &cond->ptc_waiters;
137 	pthread_spinunlock(&cond->ptc_lock);
138 
139  	/*
140  	 * Before releasing the mutex, note that this thread is
141  	 * about to block by setting the willpark flag.  If there
142  	 * is a single waiter on the mutex, setting the flag will
143  	 * defer restarting it until calling into the kernel to
144  	 * park, saving a syscall & involuntary context switch.
145  	 */
146 	self->pt_willpark = 1;
147 	pthread_mutex_unlock(mutex);
148 	(void)pthread__park(self, &cond->ptc_lock, &cond->ptc_waiters,
149 	    NULL, 1, &mutex->ptm_blocked);
150 	pthread_mutex_lock(mutex);
151 
152 	/*
153 	 * If we awoke abnormally the waiters list will have been
154 	 * made empty by the current thread (in pthread__park()),
155 	 * so we can check the value safely without locking.
156 	 *
157 	 * Otherwise, it will have been updated by whichever thread
158 	 * last issued a wakeup.
159 	 */
160 	if (PTQ_EMPTY(&cond->ptc_waiters) && cond->ptc_mutex != NULL) {
161 		pthread_spinlock(&cond->ptc_lock);
162 		if (PTQ_EMPTY(&cond->ptc_waiters))
163 			cond->ptc_mutex = NULL;
164 		pthread_spinunlock(&cond->ptc_lock);
165 	}
166 
167 	/*
168 	 * If we have cancelled then exit.  POSIX dictates that the
169 	 * mutex must be held when we action the cancellation.
170 	 */
171 	if (__predict_false(self->pt_cancel)) {
172 		if (self->pt_signalled)
173 			pthread_cond_signal(cond);
174 		pthread_exit(PTHREAD_CANCELED);
175 	}
176 
177 	return 0;
178 }
179 
180 int
181 pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
182     const struct timespec *abstime)
183 {
184 	pthread_t self;
185 	int retval;
186 
187 	pthread__error(EINVAL, "Invalid condition variable",
188 	    cond->ptc_magic == _PT_COND_MAGIC);
189 	pthread__error(EINVAL, "Invalid mutex",
190 	    mutex->ptm_magic == _PT_MUTEX_MAGIC);
191 	pthread__error(EPERM, "Mutex not locked in condition wait",
192 	    mutex->ptm_owner != NULL);
193 	pthread__error(EINVAL, "Invalid wait time",
194 	    (abstime->tv_sec >= 0) &&
195 	    (abstime->tv_nsec >= 0) && (abstime->tv_nsec < 1000000000));
196 
197 	self = pthread__self();
198 	PTHREADD_ADD(PTHREADD_COND_TIMEDWAIT);
199 
200 	/* Just hang out for a while if threads aren't running yet. */
201 	if (__predict_false(pthread__started == 0))
202 		return pthread_cond_wait_nothread(self, mutex, abstime);
203 
204 	if (__predict_false(self->pt_cancel))
205 		pthread_exit(PTHREAD_CANCELED);
206 
207 	/*
208 	 * Note this thread as waiting on the CV.  To ensure good
209 	 * performance it's critical that the spinlock is held for
210 	 * as short a time as possible - that means no system calls.
211 	 */
212 	pthread_spinlock(&cond->ptc_lock);
213 #ifdef ERRORCHECK
214 	if (cond->ptc_mutex == NULL)
215 		cond->ptc_mutex = mutex;
216 	else {
217 		pthread__error(EINVAL,
218 		    "Multiple mutexes used for condition wait",
219 		    cond->ptc_mutex == mutex);
220 	}
221 #else
222 	cond->ptc_mutex = mutex;
223 #endif
224 	PTQ_INSERT_HEAD(&cond->ptc_waiters, self, pt_sleep);
225 	self->pt_signalled = 0;
226 	self->pt_sleeponq = 1;
227 	self->pt_sleepobj = &cond->ptc_waiters;
228 	pthread_spinunlock(&cond->ptc_lock);
229 
230  	/*
231  	 * Before releasing the mutex, note that this thread is
232  	 * about to block by setting the willpark flag.  If there
233  	 * is a single waiter on the mutex, setting the flag will
234  	 * defer restarting it until calling into the kernel to
235  	 * park, saving a syscall & involuntary context switch.
236  	 */
237 	self->pt_willpark = 1;
238 	pthread_mutex_unlock(mutex);
239 	retval = pthread__park(self, &cond->ptc_lock, &cond->ptc_waiters,
240 	    abstime, 1, &mutex->ptm_blocked);
241 	pthread_mutex_lock(mutex);
242 
243 	/*
244 	 * If we awoke abnormally the waiters list will have been
245 	 * made empty by the current thread (in pthread__park()),
246 	 * so we can check the value safely without locking.
247 	 *
248 	 * Otherwise, it will have been updated by whichever thread
249 	 * last issued a wakeup.
250 	 */
251 	if (PTQ_EMPTY(&cond->ptc_waiters) && cond->ptc_mutex != NULL) {
252 		pthread_spinlock(&cond->ptc_lock);
253 		if (PTQ_EMPTY(&cond->ptc_waiters))
254 			cond->ptc_mutex = NULL;
255 		pthread_spinunlock(&cond->ptc_lock);
256 	}
257 
258 	/*
259 	 * If we have cancelled then exit.  POSIX dictates that the
260 	 * mutex must be held when we action the cancellation.
261 	 */
262 	if (__predict_false(self->pt_cancel | retval)) {
263 		if (self->pt_signalled)
264 			pthread_cond_signal(cond);
265 		if (self->pt_cancel)
266 			pthread_exit(PTHREAD_CANCELED);
267 	}
268 
269 	return retval;
270 }
271 
272 int
273 pthread_cond_signal(pthread_cond_t *cond)
274 {
275 	pthread_t self, signaled;
276 	pthread_mutex_t *mutex;
277 
278 	pthread__error(EINVAL, "Invalid condition variable",
279 	    cond->ptc_magic == _PT_COND_MAGIC);
280 	PTHREADD_ADD(PTHREADD_COND_SIGNAL);
281 
282 	if (PTQ_EMPTY(&cond->ptc_waiters))
283 		return 0;
284 
285 	self = pthread__self();
286 	pthread_spinlock(&cond->ptc_lock);
287 
288 	/*
289 	 * Find a thread that is still blocked (no pending wakeup).
290 	 * A wakeup can be pending if we have interrupted unpark_all
291 	 * as it releases the interlock.
292 	 */
293 	PTQ_FOREACH(signaled, &cond->ptc_waiters, pt_sleep) {
294 		if (signaled->pt_sleepobj != NULL)
295 			break;
296 	}
297 	if (__predict_false(signaled == NULL)) {
298 		cond->ptc_mutex = NULL;
299 		pthread_spinunlock(&cond->ptc_lock);
300 		return 0;
301 	}
302 
303 	/*
304 	 * Pull the thread off the queue, and set pt_signalled.
305 	 *
306 	 * After resuming execution, the thread must check to see if it
307 	 * has been restarted as a result of pthread_cond_signal().  If it
308 	 * has, but cannot take the wakeup (because of eg a timeout) then
309 	 * try to ensure that another thread sees it.  This is necessary
310 	 * because there may be multiple waiters, and at least one should
311 	 * take the wakeup if possible.
312 	 */
313 	PTQ_REMOVE(&cond->ptc_waiters, signaled, pt_sleep);
314 	mutex = cond->ptc_mutex;
315 	if (PTQ_EMPTY(&cond->ptc_waiters))
316 		cond->ptc_mutex = NULL;
317 	signaled->pt_signalled = 1;
318 
319 	/*
320 	 * For all valid uses of pthread_cond_signal(), the caller will
321 	 * hold the mutex that the target is using to synchronize with.
322 	 * To avoid the target awakening and immediatley blocking on the
323 	 * mutex, transfer the thread to be awoken to the current thread's
324 	 * deferred wakeup list.  The waiter will be set running when the
325 	 * caller (this thread) releases the mutex.
326 	 */
327 	if (mutex != NULL && self->pt_nwaiters < pthread__unpark_max &&
328 	    pthread__mutex_deferwake(self, mutex)) {
329 		signaled->pt_sleepobj = NULL;
330 		signaled->pt_sleeponq = 0;
331 		pthread_spinunlock(&cond->ptc_lock);
332 		self->pt_waiters[self->pt_nwaiters++] = signaled->pt_lid;
333 	} else {
334 		pthread__unpark(self, &cond->ptc_lock,
335 		    &cond->ptc_waiters, signaled);
336 	}
337 	PTHREADD_ADD(PTHREADD_COND_WOKEUP);
338 
339 	return 0;
340 }
341 
342 
343 int
344 pthread_cond_broadcast(pthread_cond_t *cond)
345 {
346 	pthread_t self, signaled, next;
347 	pthread_mutex_t *mutex;
348 
349 	pthread__error(EINVAL, "Invalid condition variable",
350 	    cond->ptc_magic == _PT_COND_MAGIC);
351 
352 	PTHREADD_ADD(PTHREADD_COND_BROADCAST);
353 
354 	if (PTQ_EMPTY(&cond->ptc_waiters))
355 		return 0;
356 
357 	self = pthread__self();
358 	pthread_spinlock(&cond->ptc_lock);
359 	mutex = cond->ptc_mutex;
360 	cond->ptc_mutex = NULL;
361 
362 	/*
363 	 * Try to defer waking threads (see pthread_cond_signal()).
364 	 * Only transfer waiters for which there is no pending wakeup.
365 	 */
366 	if (mutex != NULL && pthread__mutex_deferwake(self, mutex)) {
367 		for (signaled = PTQ_FIRST(&cond->ptc_waiters);
368 		    signaled != NULL;
369 		    signaled = next) {
370 		    	next = PTQ_NEXT(signaled, pt_sleep);
371 		    	if (__predict_false(signaled->pt_sleepobj == NULL))
372 		    		continue;
373 			if (self->pt_nwaiters == pthread__unpark_max) {
374 				/* Overflow, take the slow path. */
375 				break;
376 			}
377 		    	PTQ_REMOVE(&cond->ptc_waiters, signaled, pt_sleep);
378 			signaled->pt_sleepobj = NULL;
379 			signaled->pt_sleeponq = 0;
380 			self->pt_waiters[self->pt_nwaiters++] =
381 			    signaled->pt_lid;
382 		}
383 		if (signaled == NULL) {
384 			/* Anything more to do? */
385 			pthread_spinunlock(&cond->ptc_lock);
386 			return 0;
387 		}
388 	}
389 	pthread__unpark_all(self, &cond->ptc_lock, &cond->ptc_waiters);
390 
391 	PTHREADD_ADD(PTHREADD_COND_WOKEUP);
392 
393 	return 0;
394 
395 }
396 
397 
398 int
399 pthread_condattr_init(pthread_condattr_t *attr)
400 {
401 
402 	attr->ptca_magic = _PT_CONDATTR_MAGIC;
403 
404 	return 0;
405 }
406 
407 
408 int
409 pthread_condattr_destroy(pthread_condattr_t *attr)
410 {
411 
412 	pthread__error(EINVAL, "Invalid condition variable attribute",
413 	    attr->ptca_magic == _PT_CONDATTR_MAGIC);
414 
415 	attr->ptca_magic = _PT_CONDATTR_DEAD;
416 
417 	return 0;
418 }
419 
420 /* Utility routine to hang out for a while if threads haven't started yet. */
421 static int
422 pthread_cond_wait_nothread(pthread_t self, pthread_mutex_t *mutex,
423     const struct timespec *abstime)
424 {
425 	struct timespec now, diff;
426 	int retval;
427 
428 	if (abstime == NULL) {
429 		diff.tv_sec = 99999999;
430 		diff.tv_nsec = 0;
431 	} else {
432 		clock_gettime(CLOCK_REALTIME, &now);
433 		if  (timespeccmp(abstime, &now, <))
434 			timespecclear(&diff);
435 		else
436 			timespecsub(abstime, &now, &diff);
437 	}
438 
439 	do {
440 		pthread__testcancel(self);
441 		pthread_mutex_unlock(mutex);
442 		retval = _sys_nanosleep(&diff, NULL);
443 		pthread_mutex_lock(mutex);
444 	} while (abstime == NULL && retval == 0);
445 	pthread__testcancel(self);
446 
447 	if (retval == 0)
448 		return ETIMEDOUT;
449 	else
450 		/* spurious wakeup */
451 		return 0;
452 }
453