pthread_cond.c revision 1.37 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