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