1 /* $NetBSD: pthread_cond.c,v 1.15 2004/05/03 09:13:34 kleink 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.15 2004/05/03 09:13:34 kleink 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 if (__predict_false(self->pt_cancel)) { 149 #ifdef ERRORCHECK 150 pthread_spinlock(self, &cond->ptc_lock); 151 if (PTQ_EMPTY(&cond->ptc_waiters)) 152 cond->ptc_mutex = NULL; 153 pthread_spinunlock(self, &cond->ptc_lock); 154 #endif 155 pthread_exit(PTHREAD_CANCELED); 156 } 157 158 SDPRINTF(("(cond wait %p) Woke up on %p, mutex %p\n", 159 self, cond, mutex)); 160 161 return 0; 162 } 163 164 165 struct pthread_cond__waitarg { 166 pthread_t ptw_thread; 167 pthread_cond_t *ptw_cond; 168 }; 169 170 int 171 pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 172 const struct timespec *abstime) 173 { 174 pthread_t self; 175 struct pthread_cond__waitarg wait; 176 struct pt_alarm_t alarm; 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_lock == __SIMPLELOCK_LOCKED); 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 PTHREADD_ADD(PTHREADD_COND_TIMEDWAIT); 191 192 /* Just hang out for a while if threads aren't running yet. */ 193 if (__predict_false(pthread__started == 0)) 194 return pthread_cond_wait_nothread(self, mutex, abstime); 195 196 pthread_spinlock(self, &cond->ptc_lock); 197 wait.ptw_thread = self; 198 wait.ptw_cond = cond; 199 retval = 0; 200 SDPRINTF(("(cond timed wait %p) Waiting on %p until %d.%06ld\n", 201 self, cond, abstime->tv_sec, abstime->tv_nsec/1000)); 202 203 pthread_spinlock(self, &self->pt_statelock); 204 if (__predict_false(self->pt_cancel)) { 205 pthread_spinunlock(self, &self->pt_statelock); 206 pthread_spinunlock(self, &cond->ptc_lock); 207 pthread_exit(PTHREAD_CANCELED); 208 } 209 #ifdef ERRORCHECK 210 if (cond->ptc_mutex == NULL) 211 cond->ptc_mutex = mutex; 212 else 213 pthread__error(EINVAL, 214 "Multiple mutexes used for condition wait", 215 cond->ptc_mutex == mutex); 216 #endif 217 218 pthread__alarm_add(self, &alarm, abstime, pthread_cond_wait__callback, 219 &wait); 220 self->pt_state = PT_STATE_BLOCKED_QUEUE; 221 self->pt_sleepobj = cond; 222 self->pt_sleepq = &cond->ptc_waiters; 223 self->pt_sleeplock = &cond->ptc_lock; 224 pthread_spinunlock(self, &self->pt_statelock); 225 226 PTQ_INSERT_HEAD(&cond->ptc_waiters, self, pt_sleep); 227 pthread_mutex_unlock(mutex); 228 229 pthread__block(self, &cond->ptc_lock); 230 /* Spinlock is unlocked on return */ 231 SDPRINTF(("(cond timed wait %p) Woke up on %p, mutex %p\n", 232 self, cond)); 233 pthread__alarm_del(self, &alarm); 234 if (pthread__alarm_fired(&alarm)) 235 retval = ETIMEDOUT; 236 SDPRINTF(("(cond timed wait %p) %s\n", 237 self, (retval == ETIMEDOUT) ? "(timed out)" : "")); 238 pthread_mutex_lock(mutex); 239 if (__predict_false(self->pt_cancel)) { 240 #ifdef ERRORCHECK 241 pthread_spinlock(self, &cond->ptc_lock); 242 if (PTQ_EMPTY(&cond->ptc_waiters)) 243 cond->ptc_mutex = NULL; 244 pthread_spinunlock(self, &cond->ptc_lock); 245 #endif 246 pthread_exit(PTHREAD_CANCELED); 247 } 248 249 return retval; 250 } 251 252 static void 253 pthread_cond_wait__callback(void *arg) 254 { 255 struct pthread_cond__waitarg *a; 256 pthread_t self; 257 258 a = arg; 259 self = pthread__self(); 260 261 /* 262 * Don't dequeue and schedule the thread if it's already been 263 * queued up by a signal or broadcast (but hasn't yet run as far 264 * as pthread__alarm_del(), or we wouldn't be here, and hence can't 265 * have become blocked on some *other* queue). 266 */ 267 pthread_spinlock(self, &a->ptw_cond->ptc_lock); 268 if (a->ptw_thread->pt_state == PT_STATE_BLOCKED_QUEUE) { 269 PTQ_REMOVE(&a->ptw_cond->ptc_waiters, a->ptw_thread, pt_sleep); 270 #ifdef ERRORCHECK 271 if (PTQ_EMPTY(&a->ptw_cond->ptc_waiters)) 272 a->ptw_cond->ptc_mutex = NULL; 273 #endif 274 pthread__sched(self, a->ptw_thread); 275 } 276 pthread_spinunlock(self, &a->ptw_cond->ptc_lock); 277 } 278 279 int 280 pthread_cond_signal(pthread_cond_t *cond) 281 { 282 pthread_t self, signaled; 283 284 pthread__error(EINVAL, "Invalid condition variable", 285 cond->ptc_magic == _PT_COND_MAGIC); 286 PTHREADD_ADD(PTHREADD_COND_SIGNAL); 287 288 SDPRINTF(("(cond signal %p) Signaling %p\n", 289 pthread__self(), cond)); 290 291 if (!PTQ_EMPTY(&cond->ptc_waiters)) { 292 self = pthread__self(); 293 pthread_spinlock(self, &cond->ptc_lock); 294 signaled = PTQ_FIRST(&cond->ptc_waiters); 295 if (signaled != NULL) { 296 PTQ_REMOVE(&cond->ptc_waiters, signaled, pt_sleep); 297 pthread__sched(self, signaled); 298 PTHREADD_ADD(PTHREADD_COND_WOKEUP); 299 } 300 #ifdef ERRORCHECK 301 if (PTQ_EMPTY(&cond->ptc_waiters)) 302 cond->ptc_mutex = NULL; 303 #endif 304 pthread_spinunlock(self, &cond->ptc_lock); 305 } 306 307 return 0; 308 } 309 310 311 int 312 pthread_cond_broadcast(pthread_cond_t *cond) 313 { 314 pthread_t self; 315 struct pthread_queue_t blockedq; 316 317 pthread__error(EINVAL, "Invalid condition variable", cond->ptc_magic == _PT_COND_MAGIC); 318 319 PTHREADD_ADD(PTHREADD_COND_BROADCAST); 320 SDPRINTF(("(cond signal %p) Broadcasting %p\n", 321 pthread__self(), cond)); 322 323 if (!PTQ_EMPTY(&cond->ptc_waiters)) { 324 self = pthread__self(); 325 pthread_spinlock(self, &cond->ptc_lock); 326 blockedq = cond->ptc_waiters; 327 PTQ_INIT(&cond->ptc_waiters); 328 #ifdef ERRORCHECK 329 cond->ptc_mutex = NULL; 330 #endif 331 pthread__sched_sleepers(self, &blockedq); 332 PTHREADD_ADD(PTHREADD_COND_WOKEUP); 333 pthread_spinunlock(self, &cond->ptc_lock); 334 } 335 336 return 0; 337 338 } 339 340 341 int 342 pthread_condattr_init(pthread_condattr_t *attr) 343 { 344 345 attr->ptca_magic = _PT_CONDATTR_MAGIC; 346 347 return 0; 348 } 349 350 351 int 352 pthread_condattr_destroy(pthread_condattr_t *attr) 353 { 354 355 pthread__error(EINVAL, "Invalid condition variable attribute", 356 attr->ptca_magic == _PT_CONDATTR_MAGIC); 357 358 attr->ptca_magic = _PT_CONDATTR_DEAD; 359 360 return 0; 361 } 362 363 /* Utility routine to hang out for a while if threads haven't started yet. */ 364 static int 365 pthread_cond_wait_nothread(pthread_t self, pthread_mutex_t *mutex, 366 const struct timespec *abstime) 367 { 368 struct timeval now, tv, *tvp; 369 int retval; 370 371 if (abstime == NULL) 372 tvp = NULL; 373 else { 374 tvp = &tv; 375 gettimeofday(&now, NULL); 376 TIMESPEC_TO_TIMEVAL(tvp, abstime); 377 timersub(tvp, &now, tvp); 378 } 379 380 /* 381 * The libpthread select() wrapper has cancellation tests, but 382 * we need to have the mutex locked when testing for 383 * cancellation and unlocked while we sleep. So, skip the 384 * wrapper. 385 */ 386 pthread__testcancel(self); 387 pthread_mutex_unlock(mutex); 388 retval = _sys_select(0, NULL, NULL, NULL, tvp); 389 pthread_mutex_lock(mutex); 390 pthread__testcancel(self); 391 392 if (retval == 0) 393 return ETIMEDOUT; 394 else 395 /* spurious wakeup */ 396 return 0; 397 } 398