1 /* $NetBSD: pthread_cond.c,v 1.5 2003/01/31 04:59:40 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 43 #include "pthread.h" 44 #include "pthread_int.h" 45 46 #undef PTHREAD_COND_DEBUG 47 48 #ifdef PTHREAD_COND_DEBUG 49 #define SDPRINTF(x) DPRINTF(x) 50 #else 51 #define SDPRINTF(x) 52 #endif 53 54 static void pthread_cond_wait__callback(void *); 55 56 __strong_alias(__libc_cond_init,pthread_cond_init) 57 __strong_alias(__libc_cond_signal,pthread_cond_signal) 58 __strong_alias(__libc_cond_broadcast,pthread_cond_broadcast) 59 __strong_alias(__libc_cond_wait,pthread_cond_wait) 60 __strong_alias(__libc_cond_timedwait,pthread_cond_timedwait) 61 __strong_alias(__libc_cond_destroy,pthread_cond_destroy) 62 63 int 64 pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) 65 { 66 67 #ifdef ERRORCHECK 68 if ((cond == NULL) || 69 (attr && (attr->ptca_magic != _PT_CONDATTR_MAGIC))) 70 return EINVAL; 71 #endif 72 73 cond->ptc_magic = _PT_COND_MAGIC; 74 pthread_lockinit(&cond->ptc_lock); 75 PTQ_INIT(&cond->ptc_waiters); 76 cond->ptc_mutex = NULL; 77 78 return 0; 79 } 80 81 82 int 83 pthread_cond_destroy(pthread_cond_t *cond) 84 { 85 86 #ifdef ERRORCHECK 87 if ((cond == NULL) || (cond->ptc_magic != _PT_COND_MAGIC) || 88 (cond->ptc_mutex != NULL) || 89 (cond->ptc_lock != __SIMPLELOCK_UNLOCKED)) 90 return EINVAL; 91 #endif 92 93 cond->ptc_magic = _PT_COND_DEAD; 94 95 return 0; 96 } 97 98 99 int 100 pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 101 { 102 pthread_t self; 103 #ifdef ERRORCHECK 104 if ((cond == NULL) || (cond->ptc_magic != _PT_COND_MAGIC) || 105 (mutex == NULL) || (mutex->ptm_magic != _PT_MUTEX_MAGIC)) 106 return EINVAL; 107 #endif 108 self = pthread__self(); 109 PTHREADD_ADD(PTHREADD_COND_WAIT); 110 pthread_spinlock(self, &cond->ptc_lock); 111 #ifdef ERRORCHECK 112 if (cond->ptc_mutex == NULL) 113 cond->ptc_mutex = mutex; 114 else { 115 if (cond->ptc_mutex != mutex) { 116 pthread_spinunlock(self, &cond->ptc_lock); 117 return EINVAL; 118 } 119 /* Check the mutex is actually locked */ 120 if (mutex->ptm_lock != __SIMPLELOCK_LOCKED) { 121 pthread_spinunlock(self, &cond->ptc_lock); 122 return EPERM; 123 } 124 } 125 #endif 126 SDPRINTF(("(cond wait %p) Waiting on %p, mutex %p\n", 127 self, cond, mutex)); 128 pthread_spinlock(self, &self->pt_statelock); 129 if (self->pt_cancel) { 130 pthread_spinunlock(self, &self->pt_statelock); 131 pthread_spinunlock(self, &cond->ptc_lock); 132 pthread_exit(PTHREAD_CANCELED); 133 } 134 self->pt_state = PT_STATE_BLOCKED_QUEUE; 135 self->pt_sleepobj = cond; 136 self->pt_sleepq = &cond->ptc_waiters; 137 self->pt_sleeplock = &cond->ptc_lock; 138 pthread_spinunlock(self, &self->pt_statelock); 139 140 PTQ_INSERT_TAIL(&cond->ptc_waiters, self, pt_sleep); 141 pthread_mutex_unlock(mutex); 142 143 pthread__block(self, &cond->ptc_lock); 144 /* Spinlock is unlocked on return */ 145 pthread_mutex_lock(mutex); 146 pthread__testcancel(self); 147 SDPRINTF(("(cond wait %p) Woke up on %p, mutex %p\n", 148 self, cond, mutex)); 149 150 return 0; 151 } 152 153 154 struct pthread_cond__waitarg { 155 pthread_t ptw_thread; 156 pthread_cond_t *ptw_cond; 157 }; 158 159 int 160 pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 161 const struct timespec *abstime) 162 { 163 pthread_t self; 164 struct pthread_cond__waitarg wait; 165 struct pt_alarm_t alarm; 166 int retval; 167 168 #ifdef ERRORCHECK 169 if ((cond == NULL) || (cond->ptc_magic != _PT_COND_MAGIC) || 170 (mutex == NULL) || (mutex->ptm_magic != _PT_MUTEX_MAGIC)) 171 return EINVAL; 172 if ((abstime == NULL) || (abstime->tv_sec < 0 || 173 abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)) 174 return EINVAL; 175 #endif 176 self = pthread__self(); 177 pthread_spinlock(self, &cond->ptc_lock); 178 #ifdef ERRORCHECK 179 if (cond->ptc_mutex == NULL) 180 cond->ptc_mutex = mutex; 181 else { 182 if (cond->ptc_mutex != mutex) { 183 pthread_spinunlock(self, &cond->ptc_lock); 184 return EINVAL; 185 } 186 /* Check the mutex is actually locked */ 187 if (mutex->ptm_lock != __SIMPLELOCK_LOCKED) { 188 pthread_spinunlock(self, &cond->ptc_lock); 189 return EPERM; 190 } 191 } 192 #endif 193 PTHREADD_ADD(PTHREADD_COND_TIMEDWAIT); 194 wait.ptw_thread = self; 195 wait.ptw_cond = cond; 196 retval = 0; 197 SDPRINTF(("(cond timed wait %p) Waiting on %p until %d.%06ld\n", 198 self, cond, abstime->tv_sec, abstime->tv_nsec/1000)); 199 200 pthread_spinlock(self, &self->pt_statelock); 201 if (self->pt_cancel) { 202 pthread_spinunlock(self, &self->pt_statelock); 203 pthread_spinunlock(self, &cond->ptc_lock); 204 pthread_exit(PTHREAD_CANCELED); 205 } 206 pthread__alarm_add(self, &alarm, abstime, pthread_cond_wait__callback, 207 &wait); 208 self->pt_state = PT_STATE_BLOCKED_QUEUE; 209 self->pt_sleepobj = cond; 210 self->pt_sleepq = &cond->ptc_waiters; 211 self->pt_sleeplock = &cond->ptc_lock; 212 pthread_spinunlock(self, &self->pt_statelock); 213 214 PTQ_INSERT_TAIL(&cond->ptc_waiters, self, pt_sleep); 215 pthread_mutex_unlock(mutex); 216 217 pthread__block(self, &cond->ptc_lock); 218 /* Spinlock is unlocked on return */ 219 SDPRINTF(("(cond timed wait %p) Woke up on %p, mutex %p\n", 220 self, cond)); 221 pthread__alarm_del(self, &alarm); 222 if (pthread__alarm_fired(&alarm)) 223 retval = ETIMEDOUT; 224 SDPRINTF(("(cond timed wait %p) %s\n", 225 self, (retval == ETIMEDOUT) ? "(timed out)" : "")); 226 pthread_mutex_lock(mutex); 227 pthread__testcancel(self); 228 229 return retval; 230 } 231 232 static void 233 pthread_cond_wait__callback(void *arg) 234 { 235 struct pthread_cond__waitarg *a; 236 pthread_t self; 237 238 a = arg; 239 self = pthread__self(); 240 241 /* 242 * Don't dequeue and schedule the thread if it's already been 243 * queued up by a signal or broadcast (but hasn't yet run as far 244 * as pthread__alarm_del(), or we wouldn't be here, and hence can't 245 * have become blocked on some *other* queue). 246 */ 247 pthread_spinlock(self, &a->ptw_cond->ptc_lock); 248 if (a->ptw_thread->pt_state == PT_STATE_BLOCKED_QUEUE) { 249 PTQ_REMOVE(&a->ptw_cond->ptc_waiters, a->ptw_thread, pt_sleep); 250 #ifdef ERRORCHECK 251 if (PTQ_EMPTY(&a->ptw_cond->ptc_waiters)) 252 a->ptw_cond->ptc_mutex = NULL; 253 #endif 254 pthread__sched(self, a->ptw_thread); 255 } 256 pthread_spinunlock(self, &a->ptw_cond->ptc_lock); 257 } 258 259 int 260 pthread_cond_signal(pthread_cond_t *cond) 261 { 262 pthread_t self, signaled; 263 #ifdef ERRORCHECK 264 if ((cond == NULL) || (cond->ptc_magic != _PT_COND_MAGIC)) 265 return EINVAL; 266 #endif 267 PTHREADD_ADD(PTHREADD_COND_SIGNAL); 268 269 SDPRINTF(("(cond signal %p) Signaling %p\n", 270 pthread__self(), cond)); 271 272 if (!PTQ_EMPTY(&cond->ptc_waiters)) { 273 self = pthread__self(); 274 pthread_spinlock(self, &cond->ptc_lock); 275 signaled = PTQ_FIRST(&cond->ptc_waiters); 276 if (signaled != NULL) 277 PTQ_REMOVE(&cond->ptc_waiters, signaled, pt_sleep); 278 #ifdef ERRORCHECK 279 if (PTQ_EMPTY(&cond->ptc_waiters)) 280 cond->ptc_mutex = NULL; 281 #endif 282 pthread_spinunlock(self, &cond->ptc_lock); 283 if (signaled != NULL) 284 pthread__sched(self, signaled); 285 } 286 287 return 0; 288 } 289 290 291 int 292 pthread_cond_broadcast(pthread_cond_t *cond) 293 { 294 pthread_t self; 295 struct pthread_queue_t blockedq; 296 #ifdef ERRORCHECK 297 if ((cond == NULL) || (cond->ptc_magic != _PT_COND_MAGIC)) 298 return EINVAL; 299 #endif 300 301 PTHREADD_ADD(PTHREADD_COND_BROADCAST); 302 SDPRINTF(("(cond signal %p) Broadcasting %p\n", 303 pthread__self(), cond)); 304 305 if (!PTQ_EMPTY(&cond->ptc_waiters)) { 306 self = pthread__self(); 307 pthread_spinlock(self, &cond->ptc_lock); 308 blockedq = cond->ptc_waiters; 309 PTQ_INIT(&cond->ptc_waiters); 310 #ifdef ERRORCHECK 311 cond->ptc_mutex = NULL; 312 #endif 313 pthread_spinunlock(self, &cond->ptc_lock); 314 pthread__sched_sleepers(self, &blockedq); 315 } 316 317 return 0; 318 319 } 320 321 322 int 323 pthread_condattr_init(pthread_condattr_t *attr) 324 { 325 326 #ifdef ERRORCHECK 327 if (attr == NULL) 328 return EINVAL; 329 #endif 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 #ifdef ERRORCHECK 342 if ((attr == NULL) || 343 (attr->ptca_magic != _PT_CONDATTR_MAGIC)) 344 return EINVAL; 345 #endif 346 347 attr->ptca_magic = _PT_CONDATTR_DEAD; 348 349 return 0; 350 } 351