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