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 * $DragonFly: src/lib/libthread_xu/thread/thr_cond.c,v 1.2 2005/02/26 02:04:22 davidxu Exp $ 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 *cond = NULL; 109 return cond_init(cond, cond_attr); 110 } 111 112 int 113 _pthread_cond_destroy(pthread_cond_t *cond) 114 { 115 struct pthread_cond *cv; 116 struct pthread *curthread = _get_curthread(); 117 int rval = 0; 118 119 if (*cond == NULL) 120 rval = EINVAL; 121 else { 122 /* Lock the condition variable structure: */ 123 THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); 124 if ((*cond)->c_waiters + (*cond)->c_wakeups != 0) { 125 THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); 126 return (EBUSY); 127 } 128 129 /* 130 * NULL the caller's pointer now that the condition 131 * variable has been destroyed: 132 */ 133 cv = *cond; 134 *cond = NULL; 135 136 /* Unlock the condition variable structure: */ 137 THR_LOCK_RELEASE(curthread, &cv->c_lock); 138 139 /* Free the cond lock structure: */ 140 141 /* 142 * Free the memory allocated for the condition 143 * variable structure: 144 */ 145 free(cv); 146 147 } 148 /* Return the completion status: */ 149 return (rval); 150 } 151 152 struct cond_cancel_info 153 { 154 pthread_mutex_t *mutex; 155 pthread_cond_t *cond; 156 long seqno; 157 }; 158 159 static void 160 cond_cancel_handler(void *arg) 161 { 162 struct pthread *curthread = _get_curthread(); 163 struct cond_cancel_info *cci = (struct cond_cancel_info *)arg; 164 pthread_cond_t cv; 165 166 cv = *(cci->cond); 167 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 168 if (cv->c_seqno != cci->seqno && cv->c_wakeups != 0) { 169 if (cv->c_waiters > 0) { 170 cv->c_seqno++; 171 _thr_umtx_wake(&cv->c_seqno, 1); 172 } else 173 cv->c_wakeups--; 174 } else { 175 cv->c_waiters--; 176 } 177 THR_LOCK_RELEASE(curthread, &cv->c_lock); 178 179 _mutex_cv_lock(cci->mutex); 180 } 181 182 static int 183 cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 184 const struct timespec *abstime, int cancel) 185 { 186 struct pthread *curthread = _get_curthread(); 187 struct timespec ts, ts2, *tsp; 188 struct cond_cancel_info cci; 189 pthread_cond_t cv; 190 long seq, oldseq; 191 int oldcancel; 192 int ret = 0; 193 194 /* 195 * If the condition variable is statically initialized, 196 * perform the dynamic initialization: 197 */ 198 if (__predict_false(*cond == NULL && 199 (ret = init_static(curthread, cond)) != 0)) 200 return (ret); 201 202 cv = *cond; 203 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 204 ret = _mutex_cv_unlock(mutex); 205 if (ret) { 206 THR_LOCK_RELEASE(curthread, &cv->c_lock); 207 return (ret); 208 } 209 oldseq = seq = cv->c_seqno; 210 cci.mutex = mutex; 211 cci.cond = cond; 212 cci.seqno = oldseq; 213 214 cv->c_waiters++; 215 do { 216 THR_LOCK_RELEASE(curthread, &cv->c_lock); 217 218 if (abstime != NULL) { 219 clock_gettime(cv->c_clockid, &ts); 220 TIMESPEC_SUB(&ts2, abstime, &ts); 221 tsp = &ts2; 222 } else 223 tsp = NULL; 224 225 if (cancel) { 226 THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &cci); 227 oldcancel = _thr_cancel_enter(curthread); 228 ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp); 229 _thr_cancel_leave(curthread, oldcancel); 230 THR_CLEANUP_POP(curthread, 0); 231 } else { 232 ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp); 233 } 234 235 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 236 seq = cv->c_seqno; 237 if (abstime != NULL && ret == ETIMEDOUT) 238 break; 239 240 /* 241 * loop if we have never been told to wake up 242 * or we lost a race. 243 */ 244 } while (seq == oldseq || cv->c_wakeups == 0); 245 246 if (seq != oldseq && cv->c_wakeups != 0) { 247 cv->c_wakeups--; 248 ret = 0; 249 } else { 250 cv->c_waiters--; 251 } 252 THR_LOCK_RELEASE(curthread, &cv->c_lock); 253 _mutex_cv_lock(mutex); 254 return (ret); 255 } 256 257 int 258 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 259 { 260 261 return (cond_wait_common(cond, mutex, NULL, 0)); 262 } 263 264 __strong_reference(_pthread_cond_wait, _thr_cond_wait); 265 266 int 267 __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 268 { 269 270 return (cond_wait_common(cond, mutex, NULL, 1)); 271 } 272 273 int 274 _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 275 const struct timespec * abstime) 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 __strong_reference(_pthread_cond_timedwait, _thr_cond_timedwait); 285 286 int 287 __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 288 const struct timespec *abstime) 289 { 290 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 291 abstime->tv_nsec >= 1000000000) 292 return (EINVAL); 293 294 return (cond_wait_common(cond, mutex, abstime, 1)); 295 } 296 297 static int 298 cond_signal_common(pthread_cond_t *cond, int broadcast) 299 { 300 struct pthread *curthread = _get_curthread(); 301 pthread_cond_t cv; 302 int ret = 0; 303 304 /* 305 * If the condition variable is statically initialized, perform dynamic 306 * initialization. 307 */ 308 if (__predict_false(*cond == NULL && 309 (ret = init_static(curthread, cond)) != 0)) 310 return (ret); 311 312 cv = *cond; 313 /* Lock the condition variable structure. */ 314 THR_LOCK_ACQUIRE(curthread, &cv->c_lock); 315 if (cv->c_waiters) { 316 if (!broadcast) { 317 cv->c_wakeups++; 318 cv->c_waiters--; 319 cv->c_seqno++; 320 _thr_umtx_wake(&cv->c_seqno, 1); 321 } else { 322 cv->c_wakeups += cv->c_waiters; 323 cv->c_waiters = 0; 324 cv->c_seqno++; 325 _thr_umtx_wake(&cv->c_seqno, INT_MAX); 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 __strong_reference(_pthread_cond_signal, _thr_cond_signal); 340 341 int 342 _pthread_cond_broadcast(pthread_cond_t * cond) 343 { 344 345 return (cond_signal_common(cond, 1)); 346 } 347 348 __strong_reference(_pthread_cond_broadcast, _thr_cond_broadcast); 349