1 /* 2 * Copyright (c) 2005 David Xu <davidxu@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice unmodified, this list of conditions, and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 #include <stdlib.h> 30 #include <errno.h> 31 #include <string.h> 32 #include <pthread.h> 33 #include <limits.h> 34 35 #include "thr_private.h" 36 37 /* 38 * Prototypes 39 */ 40 static int cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); 41 static int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 42 const struct timespec *abstime, int cancel); 43 static int cond_signal_common(pthread_cond_t *cond, int broadcast); 44 45 /* 46 * Double underscore versions are cancellation points. Single underscore 47 * versions are not and are provided for libc internal usage (which 48 * shouldn't introduce cancellation points). 49 */ 50 __weak_reference(__pthread_cond_wait, pthread_cond_wait); 51 __weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait); 52 53 __weak_reference(_pthread_cond_init, pthread_cond_init); 54 __weak_reference(_pthread_cond_destroy, pthread_cond_destroy); 55 __weak_reference(_pthread_cond_signal, pthread_cond_signal); 56 __weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast); 57 58 static int 59 cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 60 { 61 pthread_cond_t pcond; 62 int rval = 0; 63 64 if ((pcond = (pthread_cond_t) 65 malloc(sizeof(struct pthread_cond))) == NULL) { 66 rval = ENOMEM; 67 } else { 68 /* 69 * Initialise the condition variable structure: 70 */ 71 _thr_umtx_init(&pcond->c_lock); 72 pcond->c_seqno = 0; 73 pcond->c_waiters = 0; 74 pcond->c_wakeups = 0; 75 if (cond_attr == NULL || *cond_attr == NULL) { 76 pcond->c_pshared = 0; 77 pcond->c_clockid = CLOCK_REALTIME; 78 } else { 79 pcond->c_pshared = (*cond_attr)->c_pshared; 80 pcond->c_clockid = (*cond_attr)->c_clockid; 81 } 82 *cond = pcond; 83 } 84 /* Return the completion status: */ 85 return (rval); 86 } 87 88 static int 89 init_static(struct pthread *thread, pthread_cond_t *cond) 90 { 91 int ret; 92 93 THR_LOCK_ACQUIRE(thread, &_cond_static_lock); 94 95 if (*cond == NULL) 96 ret = cond_init(cond, NULL); 97 else 98 ret = 0; 99 100 THR_LOCK_RELEASE(thread, &_cond_static_lock); 101 102 return (ret); 103 } 104 105 int 106 _pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 107 { 108 109 *cond = NULL; 110 return (cond_init(cond, cond_attr)); 111 } 112 113 int 114 _pthread_cond_destroy(pthread_cond_t *cond) 115 { 116 struct pthread_cond *cv; 117 struct pthread *curthread = _get_curthread(); 118 int rval = 0; 119 120 if (*cond == NULL) 121 rval = EINVAL; 122 else { 123 /* Lock the condition variable structure: */ 124 THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 125 if ((*cond)->c_waiters + (*cond)->c_wakeups != 0) { 126 THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 127 return (EBUSY); 128 } 129 130 /* 131 * NULL the caller's pointer now that the condition 132 * variable has been destroyed: 133 */ 134 cv = *cond; 135 *cond = NULL; 136 137 /* Unlock the condition variable structure: */ 138 THR_LOCK_RELEASE(curthread, &cv->c_lock); 139 140 /* Free the cond lock structure: */ 141 142 /* 143 * Free the memory allocated for the condition 144 * variable structure: 145 */ 146 free(cv); 147 148 } 149 /* Return the completion status: */ 150 return (rval); 151 } 152 153 struct cond_cancel_info 154 { 155 pthread_mutex_t *mutex; 156 pthread_cond_t *cond; 157 long seqno; 158 }; 159 160 static void 161 cond_cancel_handler(void *arg) 162 { 163 struct pthread *curthread = _get_curthread(); 164 struct cond_cancel_info *cci = (struct cond_cancel_info *)arg; 165 pthread_cond_t cv; 166 167 cv = *(cci->cond); 168 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 169 if (cv->c_seqno != cci->seqno && cv->c_wakeups != 0) { 170 if (cv->c_waiters > 0) { 171 cv->c_seqno++; 172 _thr_umtx_wake(&cv->c_seqno, 1); 173 } else 174 cv->c_wakeups--; 175 } else { 176 cv->c_waiters--; 177 } 178 THR_LOCK_RELEASE(curthread, &cv->c_lock); 179 180 _mutex_cv_lock(cci->mutex); 181 } 182 183 static int 184 cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 185 const struct timespec *abstime, int cancel) 186 { 187 struct pthread *curthread = _get_curthread(); 188 struct timespec ts, ts2, *tsp; 189 struct cond_cancel_info cci; 190 pthread_cond_t cv; 191 long seq, oldseq; 192 int oldcancel; 193 int ret = 0; 194 195 /* 196 * If the condition variable is statically initialized, 197 * perform the dynamic initialization: 198 */ 199 if (__predict_false(*cond == NULL && 200 (ret = init_static(curthread, cond)) != 0)) 201 return (ret); 202 203 cv = *cond; 204 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 205 ret = _mutex_cv_unlock(mutex); 206 if (ret) { 207 THR_LOCK_RELEASE(curthread, &cv->c_lock); 208 return (ret); 209 } 210 oldseq = seq = cv->c_seqno; 211 cci.mutex = mutex; 212 cci.cond = cond; 213 cci.seqno = oldseq; 214 215 cv->c_waiters++; 216 do { 217 THR_LOCK_RELEASE(curthread, &cv->c_lock); 218 219 if (abstime != NULL) { 220 clock_gettime(cv->c_clockid, &ts); 221 TIMESPEC_SUB(&ts2, abstime, &ts); 222 tsp = &ts2; 223 } else 224 tsp = NULL; 225 226 if (cancel) { 227 THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &cci); 228 oldcancel = _thr_cancel_enter(curthread); 229 ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp); 230 _thr_cancel_leave(curthread, oldcancel); 231 THR_CLEANUP_POP(curthread, 0); 232 } else { 233 ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp); 234 } 235 236 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 237 seq = cv->c_seqno; 238 if (abstime != NULL && ret == ETIMEDOUT) 239 break; 240 241 /* 242 * loop if we have never been told to wake up 243 * or we lost a race. 244 */ 245 } while (seq == oldseq || cv->c_wakeups == 0); 246 247 if (seq != oldseq && cv->c_wakeups != 0) { 248 cv->c_wakeups--; 249 ret = 0; 250 } else { 251 cv->c_waiters--; 252 } 253 THR_LOCK_RELEASE(curthread, &cv->c_lock); 254 _mutex_cv_lock(mutex); 255 return (ret); 256 } 257 258 int 259 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 260 { 261 262 return (cond_wait_common(cond, mutex, NULL, 0)); 263 } 264 265 int 266 __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 267 { 268 269 return (cond_wait_common(cond, mutex, NULL, 1)); 270 } 271 272 int 273 _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 274 const struct timespec * abstime) 275 { 276 277 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 278 abstime->tv_nsec >= 1000000000) 279 return (EINVAL); 280 281 return (cond_wait_common(cond, mutex, abstime, 0)); 282 } 283 284 int 285 __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 286 const struct timespec *abstime) 287 { 288 289 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 290 abstime->tv_nsec >= 1000000000) 291 return (EINVAL); 292 293 return (cond_wait_common(cond, mutex, abstime, 1)); 294 } 295 296 static int 297 cond_signal_common(pthread_cond_t *cond, int broadcast) 298 { 299 struct pthread *curthread = _get_curthread(); 300 pthread_cond_t cv; 301 int ret = 0, oldwaiters; 302 303 /* 304 * If the condition variable is statically initialized, perform dynamic 305 * initialization. 306 */ 307 if (__predict_false(*cond == NULL && 308 (ret = init_static(curthread, cond)) != 0)) 309 return (ret); 310 311 cv = *cond; 312 /* Lock the condition variable structure. */ 313 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 314 if (cv->c_waiters) { 315 if (!broadcast) { 316 cv->c_wakeups++; 317 cv->c_waiters--; 318 cv->c_seqno++; 319 _thr_umtx_wake(&cv->c_seqno, 1); 320 } else { 321 oldwaiters = cv->c_waiters; 322 cv->c_wakeups += cv->c_waiters; 323 cv->c_waiters = 0; 324 cv->c_seqno++; 325 _thr_umtx_wake(&cv->c_seqno, oldwaiters); 326 } 327 } 328 THR_LOCK_RELEASE(curthread, &cv->c_lock); 329 return (ret); 330 } 331 332 int 333 _pthread_cond_signal(pthread_cond_t * cond) 334 { 335 336 return (cond_signal_common(cond, 0)); 337 } 338 339 int 340 _pthread_cond_broadcast(pthread_cond_t * cond) 341 { 342 343 return (cond_signal_common(cond, 1)); 344 } 345