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