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