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 calloc(1, sizeof(struct pthread_cond))) == NULL) { 71 rval = ENOMEM; 72 } else { 73 /* 74 * Initialise the condition variable structure: 75 */ 76 if (cond_attr == NULL || *cond_attr == NULL) { 77 pcond->c_pshared = 0; 78 pcond->c_clockid = CLOCK_REALTIME; 79 } else { 80 pcond->c_pshared = (*cond_attr)->c_pshared; 81 pcond->c_clockid = (*cond_attr)->c_clockid; 82 } 83 _thr_umutex_init(&pcond->c_lock); 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 #define CHECK_AND_INIT_COND \ 108 if (__predict_false((cv = (*cond)) <= THR_COND_DESTROYED)) { \ 109 if (cv == THR_COND_INITIALIZER) { \ 110 int ret; \ 111 ret = init_static(_get_curthread(), cond); \ 112 if (ret) \ 113 return (ret); \ 114 } else if (cv == THR_COND_DESTROYED) { \ 115 return (EINVAL); \ 116 } \ 117 cv = *cond; \ 118 } 119 120 int 121 _pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 122 { 123 124 *cond = NULL; 125 return (cond_init(cond, cond_attr)); 126 } 127 128 int 129 _pthread_cond_destroy(pthread_cond_t *cond) 130 { 131 struct pthread *curthread = _get_curthread(); 132 struct pthread_cond *cv; 133 int rval = 0; 134 135 if ((cv = *cond) == THR_COND_INITIALIZER) 136 rval = 0; 137 else if (cv == THR_COND_DESTROYED) 138 rval = EINVAL; 139 else { 140 cv = *cond; 141 THR_UMUTEX_LOCK(curthread, &cv->c_lock); 142 *cond = THR_COND_DESTROYED; 143 THR_UMUTEX_UNLOCK(curthread, &cv->c_lock); 144 145 /* 146 * Free the memory allocated for the condition 147 * variable structure: 148 */ 149 free(cv); 150 } 151 return (rval); 152 } 153 154 struct cond_cancel_info 155 { 156 pthread_mutex_t *mutex; 157 pthread_cond_t *cond; 158 int count; 159 }; 160 161 static void 162 cond_cancel_handler(void *arg) 163 { 164 struct pthread *curthread = _get_curthread(); 165 struct cond_cancel_info *info = (struct cond_cancel_info *)arg; 166 pthread_cond_t cv; 167 168 if (info->cond != NULL) { 169 cv = *(info->cond); 170 THR_UMUTEX_UNLOCK(curthread, &cv->c_lock); 171 } 172 _mutex_cv_lock(info->mutex, info->count); 173 } 174 175 /* 176 * Cancellation behaivor: 177 * Thread may be canceled at start, if thread is canceled, it means it 178 * did not get a wakeup from pthread_cond_signal(), otherwise, it is 179 * not canceled. 180 * Thread cancellation never cause wakeup from pthread_cond_signal() 181 * to be lost. 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 info; 190 pthread_cond_t cv; 191 int ret; 192 193 /* 194 * If the condition variable is statically initialized, 195 * perform the dynamic initialization: 196 */ 197 CHECK_AND_INIT_COND 198 199 cv = *cond; 200 THR_UMUTEX_LOCK(curthread, &cv->c_lock); 201 ret = _mutex_cv_unlock(mutex, &info.count); 202 if (__predict_false(ret != 0)) { 203 THR_UMUTEX_UNLOCK(curthread, &cv->c_lock); 204 return (ret); 205 } 206 207 info.mutex = mutex; 208 info.cond = cond; 209 210 if (abstime != NULL) { 211 clock_gettime(cv->c_clockid, &ts); 212 TIMESPEC_SUB(&ts2, abstime, &ts); 213 tsp = &ts2; 214 } else 215 tsp = NULL; 216 217 if (cancel) { 218 THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info); 219 _thr_cancel_enter2(curthread, 0); 220 ret = _thr_ucond_wait(&cv->c_kerncv, &cv->c_lock, tsp, 1); 221 info.cond = NULL; 222 _thr_cancel_leave(curthread, (ret != 0)); 223 THR_CLEANUP_POP(curthread, 0); 224 } else { 225 ret = _thr_ucond_wait(&cv->c_kerncv, &cv->c_lock, tsp, 0); 226 } 227 if (ret == EINTR) 228 ret = 0; 229 _mutex_cv_lock(mutex, info.count); 230 return (ret); 231 } 232 233 int 234 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 235 { 236 237 return (cond_wait_common(cond, mutex, NULL, 0)); 238 } 239 240 int 241 __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 242 { 243 244 return (cond_wait_common(cond, mutex, NULL, 1)); 245 } 246 247 int 248 _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 249 const struct timespec * abstime) 250 { 251 252 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 253 abstime->tv_nsec >= 1000000000) 254 return (EINVAL); 255 256 return (cond_wait_common(cond, mutex, abstime, 0)); 257 } 258 259 int 260 __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 261 const struct timespec *abstime) 262 { 263 264 if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 265 abstime->tv_nsec >= 1000000000) 266 return (EINVAL); 267 268 return (cond_wait_common(cond, mutex, abstime, 1)); 269 } 270 271 static int 272 cond_signal_common(pthread_cond_t *cond, int broadcast) 273 { 274 struct pthread *curthread = _get_curthread(); 275 pthread_cond_t cv; 276 int ret = 0; 277 278 /* 279 * If the condition variable is statically initialized, perform dynamic 280 * initialization. 281 */ 282 CHECK_AND_INIT_COND 283 284 THR_UMUTEX_LOCK(curthread, &cv->c_lock); 285 if (!broadcast) 286 ret = _thr_ucond_signal(&cv->c_kerncv); 287 else 288 ret = _thr_ucond_broadcast(&cv->c_kerncv); 289 THR_UMUTEX_UNLOCK(curthread, &cv->c_lock); 290 return (ret); 291 } 292 293 int 294 _pthread_cond_signal(pthread_cond_t * cond) 295 { 296 297 return (cond_signal_common(cond, 0)); 298 } 299 300 int 301 _pthread_cond_broadcast(pthread_cond_t * cond) 302 { 303 304 return (cond_signal_common(cond, 1)); 305 } 306