xref: /netbsd-src/lib/libpthread/pthread_cond.c (revision 0920b4f20b78ab1ccd9f2312fbe10deaf000cbf3)
1 /*	$NetBSD: pthread_cond.c,v 1.34 2007/08/16 13:54:16 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.34 2007/08/16 13:54:16 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_lock == __SIMPLELOCK_LOCKED);
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 	if (cond->ptc_mutex == NULL)
123 		cond->ptc_mutex = mutex;
124 	else {
125 #ifdef ERRORCHECK
126 		pthread__error(EINVAL,
127 		    "Multiple mutexes used for condition wait",
128 		    cond->ptc_mutex == mutex);
129 #endif
130 	}
131 	PTQ_INSERT_HEAD(&cond->ptc_waiters, self, pt_sleep);
132 	self->pt_signalled = 0;
133 	self->pt_sleeponq = 1;
134 	self->pt_sleepobj = &cond->ptc_waiters;
135 	pthread_spinunlock(&cond->ptc_lock);
136 
137  	/*
138  	 * Before releasing the mutex, note that this thread is
139  	 * about to block by setting the willpark flag.  If there
140  	 * is a single waiter on the mutex, setting the flag will
141  	 * defer restarting it until calling into the kernel to
142  	 * park, saving a syscall & involuntary context switch.
143  	 */
144 	self->pt_willpark = 1;
145 	pthread_mutex_unlock(mutex);
146 	(void)pthread__park(self, &cond->ptc_lock, &cond->ptc_waiters,
147 	    NULL, 1, &mutex->ptm_blocked);
148 
149 	/*
150 	 * If we awoke abnormally the waiters list will have been
151 	 * made empty by the current thread (in pthread__park()),
152 	 * so we can check the value safely without locking.
153 	 *
154 	 * Otherwise, it will have been updated by whichever thread
155 	 * last issued a wakeup.
156 	 */
157 	if (PTQ_EMPTY(&cond->ptc_waiters) && cond->ptc_mutex != NULL) {
158 		pthread_spinlock(&cond->ptc_lock);
159 		if (PTQ_EMPTY(&cond->ptc_waiters))
160 			cond->ptc_mutex = NULL;
161 		pthread_spinunlock(&cond->ptc_lock);
162 	}
163 
164 	/*
165 	 * Re-acquire the mutex and return to the caller.  If we
166 	 * have cancelled then exit.  POSIX dictates that the mutex
167 	 * must be held when we action the cancellation.
168 	 */
169 	pthread_mutex_lock(mutex);
170 	if (__predict_false(self->pt_cancel)) {
171 		if (self->pt_signalled)
172 			pthread_cond_signal(cond);
173 		pthread_exit(PTHREAD_CANCELED);
174 	}
175 
176 	return 0;
177 }
178 
179 int
180 pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
181     const struct timespec *abstime)
182 {
183 	pthread_t self;
184 	int retval;
185 
186 	pthread__error(EINVAL, "Invalid condition variable",
187 	    cond->ptc_magic == _PT_COND_MAGIC);
188 	pthread__error(EINVAL, "Invalid mutex",
189 	    mutex->ptm_magic == _PT_MUTEX_MAGIC);
190 	pthread__error(EPERM, "Mutex not locked in condition wait",
191 	    mutex->ptm_lock == __SIMPLELOCK_LOCKED);
192 	pthread__error(EINVAL, "Invalid wait time",
193 	    (abstime->tv_sec >= 0) &&
194 	    (abstime->tv_nsec >= 0) && (abstime->tv_nsec < 1000000000));
195 
196 	self = pthread__self();
197 	PTHREADD_ADD(PTHREADD_COND_TIMEDWAIT);
198 
199 	/* Just hang out for a while if threads aren't running yet. */
200 	if (__predict_false(pthread__started == 0))
201 		return pthread_cond_wait_nothread(self, mutex, abstime);
202 
203 	if (__predict_false(self->pt_cancel))
204 		pthread_exit(PTHREAD_CANCELED);
205 
206 	/*
207 	 * Note this thread as waiting on the CV.  To ensure good
208 	 * performance it's critical that the spinlock is held for
209 	 * as short a time as possible - that means no system calls.
210 	 */
211 	pthread_spinlock(&cond->ptc_lock);
212 	if (cond->ptc_mutex == NULL)
213 		cond->ptc_mutex = mutex;
214 	else {
215 #ifdef ERRORCHECK
216 		pthread__error(EINVAL,
217 		    "Multiple mutexes used for condition wait",
218 		    cond->ptc_mutex == mutex);
219 #endif
220 	}
221 	PTQ_INSERT_HEAD(&cond->ptc_waiters, self, pt_sleep);
222 	self->pt_signalled = 0;
223 	self->pt_sleeponq = 1;
224 	self->pt_sleepobj = &cond->ptc_waiters;
225 	pthread_spinunlock(&cond->ptc_lock);
226 
227  	/*
228  	 * Before releasing the mutex, note that this thread is
229  	 * about to block by setting the willpark flag.  If there
230  	 * is a single waiter on the mutex, setting the flag will
231  	 * defer restarting it until calling into the kernel to
232  	 * park, saving a syscall & involuntary context switch.
233  	 */
234 	self->pt_willpark = 1;
235 	pthread_mutex_unlock(mutex);
236 	retval = pthread__park(self, &cond->ptc_lock, &cond->ptc_waiters,
237 	    abstime, 1, &mutex->ptm_blocked);
238 
239 	/*
240 	 * If we awoke abnormally the waiters list will have been
241 	 * made empty by the current thread (in pthread__park()),
242 	 * so we can check the value safely without locking.
243 	 *
244 	 * Otherwise, it will have been updated by whichever thread
245 	 * last issued a wakeup.
246 	 */
247 	if (PTQ_EMPTY(&cond->ptc_waiters) && cond->ptc_mutex != NULL) {
248 		pthread_spinlock(&cond->ptc_lock);
249 		if (PTQ_EMPTY(&cond->ptc_waiters))
250 			cond->ptc_mutex = NULL;
251 		pthread_spinunlock(&cond->ptc_lock);
252 	}
253 
254 	/*
255 	 * Re-acquire the mutex and return to the caller.  If we
256 	 * have cancelled then exit.  POSIX dictates that the mutex
257 	 * must be held when we action the cancellation.
258 	 */
259 	pthread_mutex_lock(mutex);
260 	if (__predict_false(self->pt_cancel | retval)) {
261 		if (self->pt_signalled)
262 			pthread_cond_signal(cond);
263 		if (self->pt_cancel)
264 			pthread_exit(PTHREAD_CANCELED);
265 	}
266 
267 	return retval;
268 }
269 
270 int
271 pthread_cond_signal(pthread_cond_t *cond)
272 {
273 	pthread_t self, signaled;
274 	pthread_mutex_t *mutex;
275 
276 	pthread__error(EINVAL, "Invalid condition variable",
277 	    cond->ptc_magic == _PT_COND_MAGIC);
278 	PTHREADD_ADD(PTHREADD_COND_SIGNAL);
279 
280 	if (PTQ_EMPTY(&cond->ptc_waiters))
281 		return 0;
282 
283 	self = pthread__self();
284 	pthread_spinlock(&cond->ptc_lock);
285 
286 	/*
287 	 * Find a thread that is still blocked (no pending wakeup).
288 	 * A wakeup can be pending if we have interrupted unpark_all
289 	 * as it releases the interlock.
290 	 */
291 	PTQ_FOREACH(signaled, &cond->ptc_waiters, pt_sleep) {
292 		if (signaled->pt_sleepobj != NULL)
293 			break;
294 	}
295 	if (__predict_false(signaled == NULL)) {
296 		cond->ptc_mutex = NULL;
297 		pthread_spinunlock(&cond->ptc_lock);
298 		return 0;
299 	}
300 
301 	/*
302 	 * Pull the thread off the queue, and set pt_signalled.
303 	 *
304 	 * After resuming execution, the thread must check to see if it
305 	 * has been restarted as a result of pthread_cond_signal().  If it
306 	 * has, but cannot take the wakeup (because of eg a timeout) then
307 	 * try to ensure that another thread sees it.  This is necessary
308 	 * because there may be multiple waiters, and at least one should
309 	 * take the wakeup if possible.
310 	 */
311 	PTQ_REMOVE(&cond->ptc_waiters, signaled, pt_sleep);
312 	mutex = cond->ptc_mutex;
313 	if (PTQ_EMPTY(&cond->ptc_waiters))
314 		cond->ptc_mutex = NULL;
315 	signaled->pt_signalled = 1;
316 
317 	/*
318 	 * For all valid uses of pthread_cond_signal(), the caller will
319 	 * hold the mutex that the target is using to synchronize with.
320 	 * To avoid the target awakening and immediatley blocking on the
321 	 * mutex, transfer the thread to be awoken to the mutex's waiters
322 	 * list.  The waiter will be set running when the caller (this
323 	 * thread) releases the mutex.
324 	 */
325 	if (self->pt_mutexhint != NULL && self->pt_mutexhint == mutex) {
326 		pthread_spinunlock(&cond->ptc_lock);
327 		pthread_spinlock(&mutex->ptm_interlock);
328 		PTQ_INSERT_HEAD(&mutex->ptm_blocked, signaled, pt_sleep);
329 		pthread_spinunlock(&mutex->ptm_interlock);
330 	} else {
331 		pthread__unpark(self, &cond->ptc_lock,
332 		    &cond->ptc_waiters, signaled);
333 	}
334 	PTHREADD_ADD(PTHREADD_COND_WOKEUP);
335 
336 	return 0;
337 }
338 
339 
340 int
341 pthread_cond_broadcast(pthread_cond_t *cond)
342 {
343 	pthread_t self, signaled, next;
344 	pthread_mutex_t *mutex;
345 
346 	pthread__error(EINVAL, "Invalid condition variable",
347 	    cond->ptc_magic == _PT_COND_MAGIC);
348 
349 	PTHREADD_ADD(PTHREADD_COND_BROADCAST);
350 
351 	if (PTQ_EMPTY(&cond->ptc_waiters))
352 		return 0;
353 
354 	self = pthread__self();
355 	pthread_spinlock(&cond->ptc_lock);
356 	mutex = cond->ptc_mutex;
357 	cond->ptc_mutex = NULL;
358 
359 	/*
360 	 * Try to transfer waiters to the mutex's waiters list (see
361 	 * pthread_cond_signal()).  Only transfer waiters for which
362 	 * there is no pending wakeup.
363 	 */
364 	if (self->pt_mutexhint != NULL && self->pt_mutexhint == mutex) {
365 		pthread_spinlock(&mutex->ptm_interlock);
366 		for (signaled = PTQ_FIRST(&cond->ptc_waiters);
367 		    signaled != NULL;
368 		    signaled = next) {
369 		    	next = PTQ_NEXT(signaled, pt_sleep);
370 		    	if (__predict_false(signaled->pt_sleepobj == NULL))
371 		    		continue;
372 		    	PTQ_REMOVE(&cond->ptc_waiters, signaled, pt_sleep);
373 		    	PTQ_INSERT_HEAD(&mutex->ptm_blocked, signaled,
374 		    	    pt_sleep);
375 		}
376 		pthread_spinunlock(&mutex->ptm_interlock);
377 		pthread_spinunlock(&cond->ptc_lock);
378 	} else
379 		pthread__unpark_all(self, &cond->ptc_lock, &cond->ptc_waiters);
380 
381 	PTHREADD_ADD(PTHREADD_COND_WOKEUP);
382 
383 	return 0;
384 
385 }
386 
387 
388 int
389 pthread_condattr_init(pthread_condattr_t *attr)
390 {
391 
392 	attr->ptca_magic = _PT_CONDATTR_MAGIC;
393 
394 	return 0;
395 }
396 
397 
398 int
399 pthread_condattr_destroy(pthread_condattr_t *attr)
400 {
401 
402 	pthread__error(EINVAL, "Invalid condition variable attribute",
403 	    attr->ptca_magic == _PT_CONDATTR_MAGIC);
404 
405 	attr->ptca_magic = _PT_CONDATTR_DEAD;
406 
407 	return 0;
408 }
409 
410 /* Utility routine to hang out for a while if threads haven't started yet. */
411 static int
412 pthread_cond_wait_nothread(pthread_t self, pthread_mutex_t *mutex,
413     const struct timespec *abstime)
414 {
415 	struct timespec now, diff;
416 	int retval;
417 
418 	if (abstime == NULL) {
419 		diff.tv_sec = 99999999;
420 		diff.tv_nsec = 0;
421 	} else {
422 		clock_gettime(CLOCK_REALTIME, &now);
423 		if  (timespeccmp(abstime, &now, <))
424 			timespecclear(&diff);
425 		else
426 			timespecsub(abstime, &now, &diff);
427 	}
428 
429 	do {
430 		pthread__testcancel(self);
431 		pthread_mutex_unlock(mutex);
432 		retval = _sys_nanosleep(&diff, NULL);
433 		pthread_mutex_lock(mutex);
434 	} while (abstime == NULL && retval == 0);
435 	pthread__testcancel(self);
436 
437 	if (retval == 0)
438 		return ETIMEDOUT;
439 	else
440 		/* spurious wakeup */
441 		return 0;
442 }
443