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 "namespace.h" 30 #include <stdlib.h> 31 #include <errno.h> 32 #include <string.h> 33 #include <pthread.h> 34 #include <limits.h> 35 #include "un-namespace.h" 36 37 #include "thr_private.h" 38 39 /* 40 * Prototypes 41 */ 42 int __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); 43 int __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 44 const struct timespec * abstime); 45 static int cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); 46 static int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 47 const struct timespec *abstime, int cancel); 48 static int cond_signal_common(pthread_cond_t *cond, int broadcast); 49 50 /* 51 * Double underscore versions are cancellation points. Single underscore 52 * versions are not and are provided for libc internal usage (which 53 * shouldn't introduce cancellation points). 54 */ 55 __weak_reference(__pthread_cond_wait, pthread_cond_wait); 56 __weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait); 57 58 __weak_reference(_pthread_cond_init, pthread_cond_init); 59 __weak_reference(_pthread_cond_destroy, pthread_cond_destroy); 60 __weak_reference(_pthread_cond_signal, pthread_cond_signal); 61 __weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast); 62 63 static int 64 cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 65 { 66 pthread_cond_t pcond; 67 int rval = 0; 68 69 if ((pcond = (pthread_cond_t) 70 malloc(sizeof(struct pthread_cond))) == NULL) { 71 rval = ENOMEM; 72 } else { 73 /* 74 * Initialise the condition variable structure: 75 */ 76 _thr_umtx_init(&pcond->c_lock); 77 pcond->c_seqno = 0; 78 pcond->c_waiters = 0; 79 pcond->c_wakeups = 0; 80 if (cond_attr == NULL || *cond_attr == NULL) { 81 pcond->c_pshared = 0; 82 pcond->c_clockid = CLOCK_REALTIME; 83 } else { 84 pcond->c_pshared = (*cond_attr)->c_pshared; 85 pcond->c_clockid = (*cond_attr)->c_clockid; 86 } 87 *cond = pcond; 88 } 89 /* Return the completion status: */ 90 return (rval); 91 } 92 93 static int 94 init_static(struct pthread *thread, pthread_cond_t *cond) 95 { 96 int ret; 97 98 THR_LOCK_ACQUIRE(thread, &_cond_static_lock); 99 100 if (*cond == NULL) 101 ret = cond_init(cond, NULL); 102 else 103 ret = 0; 104 105 THR_LOCK_RELEASE(thread, &_cond_static_lock); 106 107 return (ret); 108 } 109 110 int 111 _pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 112 { 113 114 *cond = NULL; 115 return (cond_init(cond, cond_attr)); 116 } 117 118 int 119 _pthread_cond_destroy(pthread_cond_t *cond) 120 { 121 struct pthread_cond *cv; 122 struct pthread *curthread = _get_curthread(); 123 int rval = 0; 124 125 if (*cond == NULL) 126 rval = EINVAL; 127 else { 128 /* Lock the condition variable structure: */ 129 THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 130 if ((*cond)->c_waiters + (*cond)->c_wakeups != 0) { 131 THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 132 return (EBUSY); 133 } 134 135 /* 136 * NULL the caller's pointer now that the condition 137 * variable has been destroyed: 138 */ 139 cv = *cond; 140 *cond = NULL; 141 142 /* Unlock the condition variable structure: */ 143 THR_LOCK_RELEASE(curthread, &cv->c_lock); 144 145 /* Free the cond lock structure: */ 146 147 /* 148 * Free the memory allocated for the condition 149 * variable structure: 150 */ 151 free(cv); 152 153 } 154 /* Return the completion status: */ 155 return (rval); 156 } 157 158 struct cond_cancel_info 159 { 160 pthread_mutex_t *mutex; 161 pthread_cond_t *cond; 162 long seqno; 163 int count; 164 }; 165 166 static void 167 cond_cancel_handler(void *arg) 168 { 169 struct pthread *curthread = _get_curthread(); 170 struct cond_cancel_info *info = (struct cond_cancel_info *)arg; 171 pthread_cond_t cv; 172 173 cv = *(info->cond); 174 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 175 if (cv->c_seqno != info->seqno && cv->c_wakeups != 0) { 176 if (cv->c_waiters > 0) { 177 cv->c_seqno++; 178 _thr_umtx_wake(&cv->c_seqno, 1); 179 } else 180 cv->c_wakeups--; 181 } else { 182 cv->c_waiters--; 183 } 184 THR_LOCK_RELEASE(curthread, &cv->c_lock); 185 186 _mutex_cv_lock(info->mutex, info->count); 187 } 188 189 static int 190 cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 191 const struct timespec *abstime, int cancel) 192 { 193 struct pthread *curthread = _get_curthread(); 194 struct timespec ts, ts2, *tsp; 195 struct cond_cancel_info info; 196 pthread_cond_t cv; 197 long seq, oldseq; 198 int oldcancel; 199 int ret = 0; 200 201 /* 202 * If the condition variable is statically initialized, 203 * perform the dynamic initialization: 204 */ 205 if (__predict_false(*cond == NULL && 206 (ret = init_static(curthread, cond)) != 0)) 207 return (ret); 208 209 cv = *cond; 210 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 211 ret = _mutex_cv_unlock(mutex, &info.count); 212 if (ret) { 213 THR_LOCK_RELEASE(curthread, &cv->c_lock); 214 return (ret); 215 } 216 oldseq = seq = cv->c_seqno; 217 info.mutex = mutex; 218 info.cond = cond; 219 info.seqno = oldseq; 220 221 cv->c_waiters++; 222 do { 223 THR_LOCK_RELEASE(curthread, &cv->c_lock); 224 225 if (abstime != NULL) { 226 clock_gettime(cv->c_clockid, &ts); 227 TIMESPEC_SUB(&ts2, abstime, &ts); 228 tsp = &ts2; 229 } else 230 tsp = NULL; 231 232 if (cancel) { 233 THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info); 234 oldcancel = _thr_cancel_enter(curthread); 235 ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp); 236 _thr_cancel_leave(curthread, oldcancel); 237 THR_CLEANUP_POP(curthread, 0); 238 } else { 239 ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp); 240 } 241 242 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 243 seq = cv->c_seqno; 244 if (abstime != NULL && ret == ETIMEDOUT) 245 break; 246 247 /* 248 * loop if we have never been told to wake up 249 * or we lost a race. 250 */ 251 } while (seq == oldseq || cv->c_wakeups == 0); 252 253 if (seq != oldseq && cv->c_wakeups != 0) { 254 cv->c_wakeups--; 255 ret = 0; 256 } else { 257 cv->c_waiters--; 258 } 259 THR_LOCK_RELEASE(curthread, &cv->c_lock); 260 _mutex_cv_lock(mutex, info.count); 261 return (ret); 262 } 263 264 int 265 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 266 { 267 268 return (cond_wait_common(cond, mutex, NULL, 0)); 269 } 270 271 int 272 __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 273 { 274 275 return (cond_wait_common(cond, mutex, NULL, 1)); 276 } 277 278 int 279 _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 280 const struct timespec * abstime) 281 { 282 283 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 284 abstime->tv_nsec >= 1000000000) 285 return (EINVAL); 286 287 return (cond_wait_common(cond, mutex, abstime, 0)); 288 } 289 290 int 291 __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 292 const struct timespec *abstime) 293 { 294 295 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 296 abstime->tv_nsec >= 1000000000) 297 return (EINVAL); 298 299 return (cond_wait_common(cond, mutex, abstime, 1)); 300 } 301 302 static int 303 cond_signal_common(pthread_cond_t *cond, int broadcast) 304 { 305 struct pthread *curthread = _get_curthread(); 306 pthread_cond_t cv; 307 int ret = 0, oldwaiters; 308 309 /* 310 * If the condition variable is statically initialized, perform dynamic 311 * initialization. 312 */ 313 if (__predict_false(*cond == NULL && 314 (ret = init_static(curthread, cond)) != 0)) 315 return (ret); 316 317 cv = *cond; 318 /* Lock the condition variable structure. */ 319 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 320 if (cv->c_waiters) { 321 if (!broadcast) { 322 cv->c_wakeups++; 323 cv->c_waiters--; 324 cv->c_seqno++; 325 _thr_umtx_wake(&cv->c_seqno, 1); 326 } else { 327 oldwaiters = cv->c_waiters; 328 cv->c_wakeups += cv->c_waiters; 329 cv->c_waiters = 0; 330 cv->c_seqno++; 331 _thr_umtx_wake(&cv->c_seqno, oldwaiters); 332 } 333 } 334 THR_LOCK_RELEASE(curthread, &cv->c_lock); 335 return (ret); 336 } 337 338 int 339 _pthread_cond_signal(pthread_cond_t * cond) 340 { 341 342 return (cond_signal_common(cond, 0)); 343 } 344 345 int 346 _pthread_cond_broadcast(pthread_cond_t * cond) 347 { 348 349 return (cond_signal_common(cond, 1)); 350 } 351