1 /* $NetBSD: pthread_cond.c,v 1.16 2004/07/27 21:44:48 nathanw Exp $ */ 2 3 /*- 4 * Copyright (c) 2001 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. 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.16 2004/07/27 21:44:48 nathanw 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 #ifdef PTHREAD_COND_DEBUG 50 #define SDPRINTF(x) DPRINTF(x) 51 #else 52 #define SDPRINTF(x) 53 #endif 54 55 int _sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *); 56 57 extern int pthread__started; 58 59 static void pthread_cond_wait__callback(void *); 60 static int pthread_cond_wait_nothread(pthread_t, pthread_mutex_t *, 61 const struct timespec *); 62 63 __strong_alias(__libc_cond_init,pthread_cond_init) 64 __strong_alias(__libc_cond_signal,pthread_cond_signal) 65 __strong_alias(__libc_cond_broadcast,pthread_cond_broadcast) 66 __strong_alias(__libc_cond_wait,pthread_cond_wait) 67 __strong_alias(__libc_cond_timedwait,pthread_cond_timedwait) 68 __strong_alias(__libc_cond_destroy,pthread_cond_destroy) 69 70 int 71 pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) 72 { 73 74 pthread__error(EINVAL, "Invalid condition variable attribute", 75 (attr == NULL) || (attr->ptca_magic == _PT_CONDATTR_MAGIC)); 76 77 cond->ptc_magic = _PT_COND_MAGIC; 78 pthread_lockinit(&cond->ptc_lock); 79 PTQ_INIT(&cond->ptc_waiters); 80 cond->ptc_mutex = NULL; 81 82 return 0; 83 } 84 85 86 int 87 pthread_cond_destroy(pthread_cond_t *cond) 88 { 89 90 pthread__error(EINVAL, "Invalid condition variable", 91 cond->ptc_magic == _PT_COND_MAGIC); 92 pthread__error(EBUSY, "Destroying condition variable in use", 93 cond->ptc_mutex == NULL); 94 95 cond->ptc_magic = _PT_COND_DEAD; 96 97 return 0; 98 } 99 100 101 int 102 pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 103 { 104 pthread_t self; 105 106 pthread__error(EINVAL, "Invalid condition variable", 107 cond->ptc_magic == _PT_COND_MAGIC); 108 pthread__error(EINVAL, "Invalid mutex", 109 mutex->ptm_magic == _PT_MUTEX_MAGIC); 110 pthread__error(EPERM, "Mutex not locked in condition wait", 111 mutex->ptm_lock == __SIMPLELOCK_LOCKED); 112 113 self = pthread__self(); 114 PTHREADD_ADD(PTHREADD_COND_WAIT); 115 116 /* Just hang out for a while if threads aren't running yet. */ 117 if (__predict_false(pthread__started == 0)) 118 return pthread_cond_wait_nothread(self, mutex, NULL); 119 120 pthread_spinlock(self, &cond->ptc_lock); 121 SDPRINTF(("(cond wait %p) Waiting on %p, mutex %p\n", 122 self, cond, mutex)); 123 pthread_spinlock(self, &self->pt_statelock); 124 if (__predict_false(self->pt_cancel)) { 125 pthread_spinunlock(self, &self->pt_statelock); 126 pthread_spinunlock(self, &cond->ptc_lock); 127 pthread_exit(PTHREAD_CANCELED); 128 } 129 #ifdef ERRORCHECK 130 if (cond->ptc_mutex == NULL) 131 cond->ptc_mutex = mutex; 132 else 133 pthread__error(EINVAL, 134 "Multiple mutexes used for condition wait", 135 cond->ptc_mutex == mutex); 136 #endif 137 self->pt_state = PT_STATE_BLOCKED_QUEUE; 138 self->pt_sleepobj = cond; 139 self->pt_sleepq = &cond->ptc_waiters; 140 self->pt_sleeplock = &cond->ptc_lock; 141 pthread_spinunlock(self, &self->pt_statelock); 142 PTQ_INSERT_HEAD(&cond->ptc_waiters, self, pt_sleep); 143 pthread_mutex_unlock(mutex); 144 145 pthread__block(self, &cond->ptc_lock); 146 /* Spinlock is unlocked on return */ 147 pthread_mutex_lock(mutex); 148 #ifdef ERRORCHECK 149 pthread_spinlock(self, &cond->ptc_lock); 150 if (PTQ_EMPTY(&cond->ptc_waiters)) 151 cond->ptc_mutex = NULL; 152 pthread_spinunlock(self, &cond->ptc_lock); 153 #endif 154 if (__predict_false(self->pt_cancel)) 155 pthread_exit(PTHREAD_CANCELED); 156 157 SDPRINTF(("(cond wait %p) Woke up on %p, mutex %p\n", 158 self, cond, mutex)); 159 160 return 0; 161 } 162 163 164 struct pthread_cond__waitarg { 165 pthread_t ptw_thread; 166 pthread_cond_t *ptw_cond; 167 }; 168 169 int 170 pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 171 const struct timespec *abstime) 172 { 173 pthread_t self; 174 struct pthread_cond__waitarg wait; 175 struct pt_alarm_t alarm; 176 int retval; 177 178 pthread__error(EINVAL, "Invalid condition variable", 179 cond->ptc_magic == _PT_COND_MAGIC); 180 pthread__error(EINVAL, "Invalid mutex", 181 mutex->ptm_magic == _PT_MUTEX_MAGIC); 182 pthread__error(EPERM, "Mutex not locked in condition wait", 183 mutex->ptm_lock == __SIMPLELOCK_LOCKED); 184 pthread__error(EINVAL, "Invalid wait time", 185 (abstime->tv_sec >= 0) && 186 (abstime->tv_nsec >= 0) && (abstime->tv_nsec < 1000000000)); 187 188 self = pthread__self(); 189 PTHREADD_ADD(PTHREADD_COND_TIMEDWAIT); 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 pthread_spinlock(self, &cond->ptc_lock); 196 wait.ptw_thread = self; 197 wait.ptw_cond = cond; 198 retval = 0; 199 SDPRINTF(("(cond timed wait %p) Waiting on %p until %d.%06ld\n", 200 self, cond, abstime->tv_sec, abstime->tv_nsec/1000)); 201 202 pthread_spinlock(self, &self->pt_statelock); 203 if (__predict_false(self->pt_cancel)) { 204 pthread_spinunlock(self, &self->pt_statelock); 205 pthread_spinunlock(self, &cond->ptc_lock); 206 pthread_exit(PTHREAD_CANCELED); 207 } 208 #ifdef ERRORCHECK 209 if (cond->ptc_mutex == NULL) 210 cond->ptc_mutex = mutex; 211 else 212 pthread__error(EINVAL, 213 "Multiple mutexes used for condition wait", 214 cond->ptc_mutex == mutex); 215 #endif 216 217 pthread__alarm_add(self, &alarm, abstime, pthread_cond_wait__callback, 218 &wait); 219 self->pt_state = PT_STATE_BLOCKED_QUEUE; 220 self->pt_sleepobj = cond; 221 self->pt_sleepq = &cond->ptc_waiters; 222 self->pt_sleeplock = &cond->ptc_lock; 223 pthread_spinunlock(self, &self->pt_statelock); 224 225 PTQ_INSERT_HEAD(&cond->ptc_waiters, self, pt_sleep); 226 pthread_mutex_unlock(mutex); 227 228 pthread__block(self, &cond->ptc_lock); 229 /* Spinlock is unlocked on return */ 230 SDPRINTF(("(cond timed wait %p) Woke up on %p, mutex %p\n", 231 self, cond)); 232 pthread__alarm_del(self, &alarm); 233 if (pthread__alarm_fired(&alarm)) 234 retval = ETIMEDOUT; 235 SDPRINTF(("(cond timed wait %p) %s\n", 236 self, (retval == ETIMEDOUT) ? "(timed out)" : "")); 237 pthread_mutex_lock(mutex); 238 #ifdef ERRORCHECK 239 pthread_spinlock(self, &cond->ptc_lock); 240 if (PTQ_EMPTY(&cond->ptc_waiters)) 241 cond->ptc_mutex = NULL; 242 pthread_spinunlock(self, &cond->ptc_lock); 243 #endif 244 if (__predict_false(self->pt_cancel)) 245 pthread_exit(PTHREAD_CANCELED); 246 247 return retval; 248 } 249 250 static void 251 pthread_cond_wait__callback(void *arg) 252 { 253 struct pthread_cond__waitarg *a; 254 pthread_t self; 255 256 a = arg; 257 self = pthread__self(); 258 259 /* 260 * Don't dequeue and schedule the thread if it's already been 261 * queued up by a signal or broadcast (but hasn't yet run as far 262 * as pthread__alarm_del(), or we wouldn't be here, and hence can't 263 * have become blocked on some *other* queue). 264 */ 265 pthread_spinlock(self, &a->ptw_cond->ptc_lock); 266 if (a->ptw_thread->pt_state == PT_STATE_BLOCKED_QUEUE) { 267 PTQ_REMOVE(&a->ptw_cond->ptc_waiters, a->ptw_thread, pt_sleep); 268 #ifdef ERRORCHECK 269 if (PTQ_EMPTY(&a->ptw_cond->ptc_waiters)) 270 a->ptw_cond->ptc_mutex = NULL; 271 #endif 272 pthread__sched(self, a->ptw_thread); 273 } 274 pthread_spinunlock(self, &a->ptw_cond->ptc_lock); 275 } 276 277 int 278 pthread_cond_signal(pthread_cond_t *cond) 279 { 280 pthread_t self, signaled; 281 282 pthread__error(EINVAL, "Invalid condition variable", 283 cond->ptc_magic == _PT_COND_MAGIC); 284 PTHREADD_ADD(PTHREADD_COND_SIGNAL); 285 286 SDPRINTF(("(cond signal %p) Signaling %p\n", 287 pthread__self(), cond)); 288 289 if (!PTQ_EMPTY(&cond->ptc_waiters)) { 290 self = pthread__self(); 291 pthread_spinlock(self, &cond->ptc_lock); 292 signaled = PTQ_FIRST(&cond->ptc_waiters); 293 if (signaled != NULL) { 294 PTQ_REMOVE(&cond->ptc_waiters, signaled, pt_sleep); 295 pthread__sched(self, signaled); 296 PTHREADD_ADD(PTHREADD_COND_WOKEUP); 297 } 298 #ifdef ERRORCHECK 299 if (PTQ_EMPTY(&cond->ptc_waiters)) 300 cond->ptc_mutex = NULL; 301 #endif 302 pthread_spinunlock(self, &cond->ptc_lock); 303 } 304 305 return 0; 306 } 307 308 309 int 310 pthread_cond_broadcast(pthread_cond_t *cond) 311 { 312 pthread_t self; 313 struct pthread_queue_t blockedq; 314 315 pthread__error(EINVAL, "Invalid condition variable", cond->ptc_magic == _PT_COND_MAGIC); 316 317 PTHREADD_ADD(PTHREADD_COND_BROADCAST); 318 SDPRINTF(("(cond signal %p) Broadcasting %p\n", 319 pthread__self(), cond)); 320 321 if (!PTQ_EMPTY(&cond->ptc_waiters)) { 322 self = pthread__self(); 323 pthread_spinlock(self, &cond->ptc_lock); 324 blockedq = cond->ptc_waiters; 325 PTQ_INIT(&cond->ptc_waiters); 326 #ifdef ERRORCHECK 327 cond->ptc_mutex = NULL; 328 #endif 329 pthread__sched_sleepers(self, &blockedq); 330 PTHREADD_ADD(PTHREADD_COND_WOKEUP); 331 pthread_spinunlock(self, &cond->ptc_lock); 332 } 333 334 return 0; 335 336 } 337 338 339 int 340 pthread_condattr_init(pthread_condattr_t *attr) 341 { 342 343 attr->ptca_magic = _PT_CONDATTR_MAGIC; 344 345 return 0; 346 } 347 348 349 int 350 pthread_condattr_destroy(pthread_condattr_t *attr) 351 { 352 353 pthread__error(EINVAL, "Invalid condition variable attribute", 354 attr->ptca_magic == _PT_CONDATTR_MAGIC); 355 356 attr->ptca_magic = _PT_CONDATTR_DEAD; 357 358 return 0; 359 } 360 361 /* Utility routine to hang out for a while if threads haven't started yet. */ 362 static int 363 pthread_cond_wait_nothread(pthread_t self, pthread_mutex_t *mutex, 364 const struct timespec *abstime) 365 { 366 struct timeval now, tv, *tvp; 367 int retval; 368 369 if (abstime == NULL) 370 tvp = NULL; 371 else { 372 tvp = &tv; 373 gettimeofday(&now, NULL); 374 TIMESPEC_TO_TIMEVAL(tvp, abstime); 375 timersub(tvp, &now, tvp); 376 } 377 378 /* 379 * The libpthread select() wrapper has cancellation tests, but 380 * we need to have the mutex locked when testing for 381 * cancellation and unlocked while we sleep. So, skip the 382 * wrapper. 383 */ 384 pthread__testcancel(self); 385 pthread_mutex_unlock(mutex); 386 retval = _sys_select(0, NULL, NULL, NULL, tvp); 387 pthread_mutex_lock(mutex); 388 pthread__testcancel(self); 389 390 if (retval == 0) 391 return ETIMEDOUT; 392 else 393 /* spurious wakeup */ 394 return 0; 395 } 396