1 /* $NetBSD: pthread_cond.c,v 1.47 2008/05/26 02:06:21 ad Exp $ */ 2 3 /*- 4 * Copyright (c) 2001, 2006, 2007, 2008 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 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * We assume that there will be no contention on pthread_cond_t::ptc_lock 34 * because functioning applications must call both the wait and wakeup 35 * functions while holding the same application provided mutex. The 36 * spinlock is present only to prevent libpthread causing the application 37 * to crash or malfunction as a result of corrupted data structures, in 38 * the event that the application is buggy. 39 * 40 * If there is contention on spinlock when real-time threads are in use, 41 * it could cause a deadlock due to priority inversion: the thread holding 42 * the spinlock may not get CPU time to make forward progress and release 43 * the spinlock to a higher priority thread that is waiting for it. 44 * Contention on the spinlock will only occur with buggy applications, 45 * so at the time of writing it's not considered a major bug in libpthread. 46 */ 47 48 #include <sys/cdefs.h> 49 __RCSID("$NetBSD: pthread_cond.c,v 1.47 2008/05/26 02:06:21 ad Exp $"); 50 51 #include <errno.h> 52 #include <sys/time.h> 53 #include <sys/types.h> 54 55 #include "pthread.h" 56 #include "pthread_int.h" 57 58 int _sys_nanosleep(const struct timespec *, struct timespec *); 59 60 extern int pthread__started; 61 62 static int pthread_cond_wait_nothread(pthread_t, pthread_mutex_t *, 63 const struct timespec *); 64 65 __strong_alias(__libc_cond_init,pthread_cond_init) 66 __strong_alias(__libc_cond_signal,pthread_cond_signal) 67 __strong_alias(__libc_cond_broadcast,pthread_cond_broadcast) 68 __strong_alias(__libc_cond_wait,pthread_cond_wait) 69 __strong_alias(__libc_cond_timedwait,pthread_cond_timedwait) 70 __strong_alias(__libc_cond_destroy,pthread_cond_destroy) 71 72 int 73 pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) 74 { 75 76 pthread__error(EINVAL, "Invalid condition variable attribute", 77 (attr == NULL) || (attr->ptca_magic == _PT_CONDATTR_MAGIC)); 78 79 cond->ptc_magic = _PT_COND_MAGIC; 80 pthread_lockinit(&cond->ptc_lock); 81 PTQ_INIT(&cond->ptc_waiters); 82 cond->ptc_mutex = NULL; 83 84 return 0; 85 } 86 87 88 int 89 pthread_cond_destroy(pthread_cond_t *cond) 90 { 91 92 pthread__error(EINVAL, "Invalid condition variable", 93 cond->ptc_magic == _PT_COND_MAGIC); 94 pthread__error(EBUSY, "Destroying condition variable in use", 95 cond->ptc_mutex == NULL); 96 97 cond->ptc_magic = _PT_COND_DEAD; 98 99 return 0; 100 } 101 102 inline int 103 pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 104 const struct timespec *abstime) 105 { 106 pthread_t self; 107 int retval; 108 109 pthread__error(EINVAL, "Invalid condition variable", 110 cond->ptc_magic == _PT_COND_MAGIC); 111 pthread__error(EINVAL, "Invalid mutex", 112 mutex->ptm_magic == _PT_MUTEX_MAGIC); 113 pthread__error(EPERM, "Mutex not locked in condition wait", 114 mutex->ptm_owner != NULL); 115 if (abstime != NULL) { 116 pthread__error(EINVAL, "Invalid wait time", 117 (abstime->tv_sec >= 0) && 118 (abstime->tv_nsec >= 0) && 119 (abstime->tv_nsec < 1000000000)); 120 } 121 122 self = pthread__self(); 123 124 /* Just hang out for a while if threads aren't running yet. */ 125 if (__predict_false(pthread__started == 0)) { 126 return pthread_cond_wait_nothread(self, mutex, abstime); 127 } 128 if (__predict_false(self->pt_cancel)) { 129 pthread__cancelled(); 130 } 131 132 /* Note this thread as waiting on the CV. */ 133 pthread__spinlock(self, &cond->ptc_lock); 134 cond->ptc_mutex = mutex; 135 PTQ_INSERT_HEAD(&cond->ptc_waiters, self, pt_sleep); 136 self->pt_sleepobj = cond; 137 pthread__spinunlock(self, &cond->ptc_lock); 138 139 do { 140 self->pt_willpark = 1; 141 pthread_mutex_unlock(mutex); 142 self->pt_willpark = 0; 143 self->pt_blocking++; 144 retval = _lwp_park(abstime, self->pt_unpark, 145 __UNVOLATILE(&mutex->ptm_waiters), 146 __UNVOLATILE(&mutex->ptm_waiters)); 147 self->pt_unpark = 0; 148 self->pt_blocking--; 149 membar_sync(); 150 pthread_mutex_lock(mutex); 151 152 /* 153 * If we have cancelled then exit. POSIX dictates that 154 * the mutex must be held when we action the cancellation. 155 * 156 * If we absorbed a pthread_cond_signal() and cannot take 157 * the wakeup, we must ensure that another thread does. 158 * 159 * If awoke early, we may still be on the sleep queue and 160 * must remove ourself. 161 */ 162 if (__predict_false(retval != 0)) { 163 switch (errno) { 164 case EINTR: 165 case EALREADY: 166 retval = 0; 167 break; 168 default: 169 retval = errno; 170 break; 171 } 172 } 173 if (__predict_false(self->pt_cancel | retval)) { 174 pthread_cond_broadcast(cond); 175 if (self->pt_cancel) { 176 pthread__cancelled(); 177 } 178 break; 179 } 180 } while (self->pt_sleepobj != NULL); 181 182 return retval; 183 } 184 185 int 186 pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 187 { 188 189 return pthread_cond_timedwait(cond, mutex, NULL); 190 } 191 192 int 193 pthread_cond_signal(pthread_cond_t *cond) 194 { 195 pthread_t self, signaled; 196 pthread_mutex_t *mutex; 197 lwpid_t lid; 198 199 if (PTQ_EMPTY(&cond->ptc_waiters)) 200 return 0; 201 202 pthread__error(EINVAL, "Invalid condition variable", 203 cond->ptc_magic == _PT_COND_MAGIC); 204 205 /* Pull the first thread off the queue. */ 206 self = pthread__self(); 207 pthread__spinlock(self, &cond->ptc_lock); 208 signaled = PTQ_FIRST(&cond->ptc_waiters); 209 if (__predict_false(signaled == NULL)) { 210 pthread__spinunlock(self, &cond->ptc_lock); 211 return 0; 212 } 213 mutex = cond->ptc_mutex; 214 if (PTQ_NEXT(signaled, pt_sleep) == NULL) { 215 cond->ptc_mutex = NULL; 216 PTQ_INIT(&cond->ptc_waiters); 217 } else { 218 PTQ_REMOVE(&cond->ptc_waiters, signaled, pt_sleep); 219 } 220 signaled->pt_sleepobj = NULL; 221 lid = signaled->pt_lid; 222 pthread__spinunlock(self, &cond->ptc_lock); 223 224 /* 225 * For all valid uses of pthread_cond_signal(), the caller will 226 * hold the mutex that the target is using to synchronize with. 227 * To avoid the target awakening and immediatley blocking on the 228 * mutex, transfer the thread to be awoken to the current thread's 229 * deferred wakeup list. The waiter will be set running when the 230 * caller (this thread) releases the mutex. 231 */ 232 if (__predict_false(self->pt_nwaiters == pthread__unpark_max)) { 233 (void)_lwp_unpark_all(self->pt_waiters, self->pt_nwaiters, 234 __UNVOLATILE(&mutex->ptm_waiters)); 235 self->pt_nwaiters = 0; 236 } 237 self->pt_waiters[self->pt_nwaiters++] = lid; 238 pthread__mutex_deferwake(self, mutex); 239 return 0; 240 } 241 242 int 243 pthread_cond_broadcast(pthread_cond_t *cond) 244 { 245 pthread_t self, signaled; 246 pthread_mutex_t *mutex; 247 u_int max, nwaiters; 248 249 if (PTQ_EMPTY(&cond->ptc_waiters)) 250 return 0; 251 252 pthread__error(EINVAL, "Invalid condition variable", 253 cond->ptc_magic == _PT_COND_MAGIC); 254 255 /* 256 * Try to defer waking threads (see pthread_cond_signal()). 257 * Only transfer waiters for which there is no pending wakeup. 258 */ 259 self = pthread__self(); 260 pthread__spinlock(self, &cond->ptc_lock); 261 max = pthread__unpark_max; 262 mutex = cond->ptc_mutex; 263 nwaiters = self->pt_nwaiters; 264 PTQ_FOREACH(signaled, &cond->ptc_waiters, pt_sleep) { 265 if (nwaiters == max) { 266 /* Overflow. */ 267 (void)_lwp_unpark_all(self->pt_waiters, 268 nwaiters, __UNVOLATILE(&mutex->ptm_waiters)); 269 nwaiters = 0; 270 } 271 signaled->pt_sleepobj = NULL; 272 self->pt_waiters[nwaiters++] = signaled->pt_lid; 273 } 274 PTQ_INIT(&cond->ptc_waiters); 275 self->pt_nwaiters = nwaiters; 276 cond->ptc_mutex = NULL; 277 pthread__spinunlock(self, &cond->ptc_lock); 278 pthread__mutex_deferwake(self, mutex); 279 280 return 0; 281 } 282 283 284 int 285 pthread_condattr_init(pthread_condattr_t *attr) 286 { 287 288 attr->ptca_magic = _PT_CONDATTR_MAGIC; 289 290 return 0; 291 } 292 293 294 int 295 pthread_condattr_destroy(pthread_condattr_t *attr) 296 { 297 298 pthread__error(EINVAL, "Invalid condition variable attribute", 299 attr->ptca_magic == _PT_CONDATTR_MAGIC); 300 301 attr->ptca_magic = _PT_CONDATTR_DEAD; 302 303 return 0; 304 } 305 306 /* Utility routine to hang out for a while if threads haven't started yet. */ 307 static int 308 pthread_cond_wait_nothread(pthread_t self, pthread_mutex_t *mutex, 309 const struct timespec *abstime) 310 { 311 struct timespec now, diff; 312 int retval; 313 314 if (abstime == NULL) { 315 diff.tv_sec = 99999999; 316 diff.tv_nsec = 0; 317 } else { 318 clock_gettime(CLOCK_REALTIME, &now); 319 if (timespeccmp(abstime, &now, <)) 320 timespecclear(&diff); 321 else 322 timespecsub(abstime, &now, &diff); 323 } 324 325 do { 326 pthread__testcancel(self); 327 pthread_mutex_unlock(mutex); 328 retval = _sys_nanosleep(&diff, NULL); 329 pthread_mutex_lock(mutex); 330 } while (abstime == NULL && retval == 0); 331 pthread__testcancel(self); 332 333 if (retval == 0) 334 return ETIMEDOUT; 335 else 336 /* spurious wakeup */ 337 return 0; 338 } 339