xref: /netbsd-src/lib/libpthread/pthread_cond.c (revision 4b71a66d0f279143147d63ebfcfd8a59499a3684)
1 /*	$NetBSD: pthread_cond.c,v 1.47 2008/05/26 02:06:21 ad Exp $	*/
2 
3 /*-
4  * Copyright (c) 2001, 2006, 2007, 2008 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 /*
33  * We assume that there will be no contention on pthread_cond_t::ptc_lock
34  * because functioning applications must call both the wait and wakeup
35  * functions while holding the same application provided mutex.  The
36  * spinlock is present only to prevent libpthread causing the application
37  * to crash or malfunction as a result of corrupted data structures, in
38  * the event that the application is buggy.
39  *
40  * If there is contention on spinlock when real-time threads are in use,
41  * it could cause a deadlock due to priority inversion: the thread holding
42  * the spinlock may not get CPU time to make forward progress and release
43  * the spinlock to a higher priority thread that is waiting for it.
44  * Contention on the spinlock will only occur with buggy applications,
45  * so at the time of writing it's not considered a major bug in libpthread.
46  */
47 
48 #include <sys/cdefs.h>
49 __RCSID("$NetBSD: pthread_cond.c,v 1.47 2008/05/26 02:06:21 ad Exp $");
50 
51 #include <errno.h>
52 #include <sys/time.h>
53 #include <sys/types.h>
54 
55 #include "pthread.h"
56 #include "pthread_int.h"
57 
58 int	_sys_nanosleep(const struct timespec *, struct timespec *);
59 
60 extern int pthread__started;
61 
62 static int pthread_cond_wait_nothread(pthread_t, pthread_mutex_t *,
63     const struct timespec *);
64 
65 __strong_alias(__libc_cond_init,pthread_cond_init)
66 __strong_alias(__libc_cond_signal,pthread_cond_signal)
67 __strong_alias(__libc_cond_broadcast,pthread_cond_broadcast)
68 __strong_alias(__libc_cond_wait,pthread_cond_wait)
69 __strong_alias(__libc_cond_timedwait,pthread_cond_timedwait)
70 __strong_alias(__libc_cond_destroy,pthread_cond_destroy)
71 
72 int
73 pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr)
74 {
75 
76 	pthread__error(EINVAL, "Invalid condition variable attribute",
77 	    (attr == NULL) || (attr->ptca_magic == _PT_CONDATTR_MAGIC));
78 
79 	cond->ptc_magic = _PT_COND_MAGIC;
80 	pthread_lockinit(&cond->ptc_lock);
81 	PTQ_INIT(&cond->ptc_waiters);
82 	cond->ptc_mutex = NULL;
83 
84 	return 0;
85 }
86 
87 
88 int
89 pthread_cond_destroy(pthread_cond_t *cond)
90 {
91 
92 	pthread__error(EINVAL, "Invalid condition variable",
93 	    cond->ptc_magic == _PT_COND_MAGIC);
94 	pthread__error(EBUSY, "Destroying condition variable in use",
95 	    cond->ptc_mutex == NULL);
96 
97 	cond->ptc_magic = _PT_COND_DEAD;
98 
99 	return 0;
100 }
101 
102 inline int
103 pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
104 		       const struct timespec *abstime)
105 {
106 	pthread_t self;
107 	int retval;
108 
109 	pthread__error(EINVAL, "Invalid condition variable",
110 	    cond->ptc_magic == _PT_COND_MAGIC);
111 	pthread__error(EINVAL, "Invalid mutex",
112 	    mutex->ptm_magic == _PT_MUTEX_MAGIC);
113 	pthread__error(EPERM, "Mutex not locked in condition wait",
114 	    mutex->ptm_owner != NULL);
115 	if (abstime != NULL) {
116 		pthread__error(EINVAL, "Invalid wait time",
117 		    (abstime->tv_sec >= 0) &&
118 		    (abstime->tv_nsec >= 0) &&
119 		    (abstime->tv_nsec < 1000000000));
120 	}
121 
122 	self = pthread__self();
123 
124 	/* Just hang out for a while if threads aren't running yet. */
125 	if (__predict_false(pthread__started == 0)) {
126 		return pthread_cond_wait_nothread(self, mutex, abstime);
127 	}
128 	if (__predict_false(self->pt_cancel)) {
129 		pthread__cancelled();
130 	}
131 
132 	/* Note this thread as waiting on the CV. */
133 	pthread__spinlock(self, &cond->ptc_lock);
134 	cond->ptc_mutex = mutex;
135 	PTQ_INSERT_HEAD(&cond->ptc_waiters, self, pt_sleep);
136 	self->pt_sleepobj = cond;
137 	pthread__spinunlock(self, &cond->ptc_lock);
138 
139 	do {
140 		self->pt_willpark = 1;
141 		pthread_mutex_unlock(mutex);
142 		self->pt_willpark = 0;
143 		self->pt_blocking++;
144 		retval = _lwp_park(abstime, self->pt_unpark,
145 		    __UNVOLATILE(&mutex->ptm_waiters),
146 		    __UNVOLATILE(&mutex->ptm_waiters));
147 		self->pt_unpark = 0;
148 		self->pt_blocking--;
149 		membar_sync();
150 		pthread_mutex_lock(mutex);
151 
152 		/*
153 		 * If we have cancelled then exit.  POSIX dictates that
154 		 * the mutex must be held when we action the cancellation.
155 		 *
156 		 * If we absorbed a pthread_cond_signal() and cannot take
157 		 * the wakeup, we must ensure that another thread does.
158 		 *
159 		 * If awoke early, we may still be on the sleep queue and
160 		 * must remove ourself.
161 		 */
162 		if (__predict_false(retval != 0)) {
163 			switch (errno) {
164 			case EINTR:
165 			case EALREADY:
166 				retval = 0;
167 				break;
168 			default:
169 				retval = errno;
170 				break;
171 			}
172 		}
173 		if (__predict_false(self->pt_cancel | retval)) {
174 			pthread_cond_broadcast(cond);
175 			if (self->pt_cancel) {
176 				pthread__cancelled();
177 			}
178 			break;
179 		}
180 	} while (self->pt_sleepobj != NULL);
181 
182 	return retval;
183 }
184 
185 int
186 pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
187 {
188 
189 	return pthread_cond_timedwait(cond, mutex, NULL);
190 }
191 
192 int
193 pthread_cond_signal(pthread_cond_t *cond)
194 {
195 	pthread_t self, signaled;
196 	pthread_mutex_t *mutex;
197 	lwpid_t lid;
198 
199 	if (PTQ_EMPTY(&cond->ptc_waiters))
200 		return 0;
201 
202 	pthread__error(EINVAL, "Invalid condition variable",
203 	    cond->ptc_magic == _PT_COND_MAGIC);
204 
205 	/* Pull the first thread off the queue. */
206 	self = pthread__self();
207 	pthread__spinlock(self, &cond->ptc_lock);
208 	signaled = PTQ_FIRST(&cond->ptc_waiters);
209 	if (__predict_false(signaled == NULL)) {
210 		pthread__spinunlock(self, &cond->ptc_lock);
211 		return 0;
212 	}
213 	mutex = cond->ptc_mutex;
214 	if (PTQ_NEXT(signaled, pt_sleep) == NULL) {
215 		cond->ptc_mutex = NULL;
216 		PTQ_INIT(&cond->ptc_waiters);
217 	} else {
218 		PTQ_REMOVE(&cond->ptc_waiters, signaled, pt_sleep);
219 	}
220 	signaled->pt_sleepobj = NULL;
221 	lid = signaled->pt_lid;
222 	pthread__spinunlock(self, &cond->ptc_lock);
223 
224 	/*
225 	 * For all valid uses of pthread_cond_signal(), the caller will
226 	 * hold the mutex that the target is using to synchronize with.
227 	 * To avoid the target awakening and immediatley blocking on the
228 	 * mutex, transfer the thread to be awoken to the current thread's
229 	 * deferred wakeup list.  The waiter will be set running when the
230 	 * caller (this thread) releases the mutex.
231 	 */
232 	if (__predict_false(self->pt_nwaiters == pthread__unpark_max)) {
233 		(void)_lwp_unpark_all(self->pt_waiters, self->pt_nwaiters,
234 		    __UNVOLATILE(&mutex->ptm_waiters));
235 		self->pt_nwaiters = 0;
236 	}
237 	self->pt_waiters[self->pt_nwaiters++] = lid;
238 	pthread__mutex_deferwake(self, mutex);
239 	return 0;
240 }
241 
242 int
243 pthread_cond_broadcast(pthread_cond_t *cond)
244 {
245 	pthread_t self, signaled;
246 	pthread_mutex_t *mutex;
247 	u_int max, nwaiters;
248 
249 	if (PTQ_EMPTY(&cond->ptc_waiters))
250 		return 0;
251 
252 	pthread__error(EINVAL, "Invalid condition variable",
253 	    cond->ptc_magic == _PT_COND_MAGIC);
254 
255 	/*
256 	 * Try to defer waking threads (see pthread_cond_signal()).
257 	 * Only transfer waiters for which there is no pending wakeup.
258 	 */
259 	self = pthread__self();
260 	pthread__spinlock(self, &cond->ptc_lock);
261 	max = pthread__unpark_max;
262 	mutex = cond->ptc_mutex;
263 	nwaiters = self->pt_nwaiters;
264 	PTQ_FOREACH(signaled, &cond->ptc_waiters, pt_sleep) {
265 		if (nwaiters == max) {
266 			/* Overflow. */
267 			(void)_lwp_unpark_all(self->pt_waiters,
268 			    nwaiters, __UNVOLATILE(&mutex->ptm_waiters));
269 			nwaiters = 0;
270 		}
271 		signaled->pt_sleepobj = NULL;
272 		self->pt_waiters[nwaiters++] = signaled->pt_lid;
273 	}
274 	PTQ_INIT(&cond->ptc_waiters);
275 	self->pt_nwaiters = nwaiters;
276 	cond->ptc_mutex = NULL;
277 	pthread__spinunlock(self, &cond->ptc_lock);
278 	pthread__mutex_deferwake(self, mutex);
279 
280 	return 0;
281 }
282 
283 
284 int
285 pthread_condattr_init(pthread_condattr_t *attr)
286 {
287 
288 	attr->ptca_magic = _PT_CONDATTR_MAGIC;
289 
290 	return 0;
291 }
292 
293 
294 int
295 pthread_condattr_destroy(pthread_condattr_t *attr)
296 {
297 
298 	pthread__error(EINVAL, "Invalid condition variable attribute",
299 	    attr->ptca_magic == _PT_CONDATTR_MAGIC);
300 
301 	attr->ptca_magic = _PT_CONDATTR_DEAD;
302 
303 	return 0;
304 }
305 
306 /* Utility routine to hang out for a while if threads haven't started yet. */
307 static int
308 pthread_cond_wait_nothread(pthread_t self, pthread_mutex_t *mutex,
309     const struct timespec *abstime)
310 {
311 	struct timespec now, diff;
312 	int retval;
313 
314 	if (abstime == NULL) {
315 		diff.tv_sec = 99999999;
316 		diff.tv_nsec = 0;
317 	} else {
318 		clock_gettime(CLOCK_REALTIME, &now);
319 		if  (timespeccmp(abstime, &now, <))
320 			timespecclear(&diff);
321 		else
322 			timespecsub(abstime, &now, &diff);
323 	}
324 
325 	do {
326 		pthread__testcancel(self);
327 		pthread_mutex_unlock(mutex);
328 		retval = _sys_nanosleep(&diff, NULL);
329 		pthread_mutex_lock(mutex);
330 	} while (abstime == NULL && retval == 0);
331 	pthread__testcancel(self);
332 
333 	if (retval == 0)
334 		return ETIMEDOUT;
335 	else
336 		/* spurious wakeup */
337 		return 0;
338 }
339