1bb535300SJeff Roberson /* 2a091d823SDavid Xu * Copyright (c) 2005 David Xu <davidxu@freebsd.org> 3bb535300SJeff Roberson * All rights reserved. 4bb535300SJeff Roberson * 5bb535300SJeff Roberson * Redistribution and use in source and binary forms, with or without 6bb535300SJeff Roberson * modification, are permitted provided that the following conditions 7bb535300SJeff Roberson * are met: 8bb535300SJeff Roberson * 1. Redistributions of source code must retain the above copyright 9a091d823SDavid Xu * notice unmodified, this list of conditions, and the following 10a091d823SDavid Xu * disclaimer. 11bb535300SJeff Roberson * 2. Redistributions in binary form must reproduce the above copyright 12bb535300SJeff Roberson * notice, this list of conditions and the following disclaimer in the 13bb535300SJeff Roberson * documentation and/or other materials provided with the distribution. 14bb535300SJeff Roberson * 15a091d823SDavid Xu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16a091d823SDavid Xu * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17a091d823SDavid Xu * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18a091d823SDavid Xu * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19a091d823SDavid Xu * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20a091d823SDavid Xu * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21a091d823SDavid Xu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22a091d823SDavid Xu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23a091d823SDavid Xu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24a091d823SDavid Xu * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25bb535300SJeff Roberson * 26bb535300SJeff Roberson * $FreeBSD$ 27bb535300SJeff Roberson */ 28a091d823SDavid Xu 2937a6356bSDavid Xu #include "namespace.h" 30bb535300SJeff Roberson #include <stdlib.h> 31bb535300SJeff Roberson #include <errno.h> 32bb535300SJeff Roberson #include <string.h> 33bb535300SJeff Roberson #include <pthread.h> 34a091d823SDavid Xu #include <limits.h> 3537a6356bSDavid Xu #include "un-namespace.h" 36bb535300SJeff Roberson 37a091d823SDavid Xu #include "thr_private.h" 3841f2bd85SMike Makonnen 3941f2bd85SMike Makonnen /* 40bb535300SJeff Roberson * Prototypes 41bb535300SJeff Roberson */ 4237a6356bSDavid Xu int __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); 4337a6356bSDavid Xu int __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 4437a6356bSDavid Xu const struct timespec * abstime); 45a091d823SDavid Xu static int cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); 46a091d823SDavid Xu static int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 47a091d823SDavid Xu const struct timespec *abstime, int cancel); 48a091d823SDavid Xu static int cond_signal_common(pthread_cond_t *cond, int broadcast); 49a091d823SDavid Xu 50a091d823SDavid Xu /* 51a091d823SDavid Xu * Double underscore versions are cancellation points. Single underscore 52a091d823SDavid Xu * versions are not and are provided for libc internal usage (which 53a091d823SDavid Xu * shouldn't introduce cancellation points). 54a091d823SDavid Xu */ 55a091d823SDavid Xu __weak_reference(__pthread_cond_wait, pthread_cond_wait); 56a091d823SDavid Xu __weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait); 57bb535300SJeff Roberson 58bb535300SJeff Roberson __weak_reference(_pthread_cond_init, pthread_cond_init); 59bb535300SJeff Roberson __weak_reference(_pthread_cond_destroy, pthread_cond_destroy); 60bb535300SJeff Roberson __weak_reference(_pthread_cond_signal, pthread_cond_signal); 61bb535300SJeff Roberson __weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast); 62bb535300SJeff Roberson 63a091d823SDavid Xu static int 64a091d823SDavid Xu cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 65bb535300SJeff Roberson { 66a091d823SDavid Xu pthread_cond_t pcond; 67a091d823SDavid Xu int rval = 0; 68a091d823SDavid Xu 69a091d823SDavid Xu if ((pcond = (pthread_cond_t) 702bd2c907SDavid Xu calloc(1, sizeof(struct pthread_cond))) == NULL) { 71a091d823SDavid Xu rval = ENOMEM; 72a091d823SDavid Xu } else { 73a091d823SDavid Xu /* 74a091d823SDavid Xu * Initialise the condition variable structure: 75a091d823SDavid Xu */ 76a091d823SDavid Xu if (cond_attr == NULL || *cond_attr == NULL) { 77a091d823SDavid Xu pcond->c_pshared = 0; 78a091d823SDavid Xu pcond->c_clockid = CLOCK_REALTIME; 79a091d823SDavid Xu } else { 80a091d823SDavid Xu pcond->c_pshared = (*cond_attr)->c_pshared; 81a091d823SDavid Xu pcond->c_clockid = (*cond_attr)->c_clockid; 82a091d823SDavid Xu } 832bd2c907SDavid Xu _thr_umutex_init(&pcond->c_lock); 84a091d823SDavid Xu *cond = pcond; 85a091d823SDavid Xu } 86a091d823SDavid Xu /* Return the completion status: */ 87a091d823SDavid Xu return (rval); 88a091d823SDavid Xu } 89a091d823SDavid Xu 90a091d823SDavid Xu static int 91a091d823SDavid Xu init_static(struct pthread *thread, pthread_cond_t *cond) 92a091d823SDavid Xu { 93a091d823SDavid Xu int ret; 94a091d823SDavid Xu 95a091d823SDavid Xu THR_LOCK_ACQUIRE(thread, &_cond_static_lock); 96bb535300SJeff Roberson 97bb535300SJeff Roberson if (*cond == NULL) 98a091d823SDavid Xu ret = cond_init(cond, NULL); 99a091d823SDavid Xu else 100a091d823SDavid Xu ret = 0; 101bb535300SJeff Roberson 102a091d823SDavid Xu THR_LOCK_RELEASE(thread, &_cond_static_lock); 103bb535300SJeff Roberson 104a091d823SDavid Xu return (ret); 105bb535300SJeff Roberson } 106bb535300SJeff Roberson 107bb535300SJeff Roberson int 108bb535300SJeff Roberson _pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 109bb535300SJeff Roberson { 110bb535300SJeff Roberson 111a091d823SDavid Xu *cond = NULL; 112a091d823SDavid Xu return (cond_init(cond, cond_attr)); 113bb535300SJeff Roberson } 114bb535300SJeff Roberson 115bb535300SJeff Roberson int 116bb535300SJeff Roberson _pthread_cond_destroy(pthread_cond_t *cond) 117bb535300SJeff Roberson { 118a091d823SDavid Xu struct pthread *curthread = _get_curthread(); 1192bd2c907SDavid Xu struct pthread_cond *cv; 120a091d823SDavid Xu int rval = 0; 121bb535300SJeff Roberson 122a091d823SDavid Xu if (*cond == NULL) 123a091d823SDavid Xu rval = EINVAL; 124a091d823SDavid Xu else { 1252bd2c907SDavid Xu cv = *cond; 1262bd2c907SDavid Xu THR_UMUTEX_LOCK(curthread, &cv->c_lock); 127bb535300SJeff Roberson /* 128bb535300SJeff Roberson * NULL the caller's pointer now that the condition 129bb535300SJeff Roberson * variable has been destroyed: 130bb535300SJeff Roberson */ 131bb535300SJeff Roberson *cond = NULL; 1322bd2c907SDavid Xu THR_UMUTEX_UNLOCK(curthread, &cv->c_lock); 133a091d823SDavid Xu 134a091d823SDavid Xu /* 135a091d823SDavid Xu * Free the memory allocated for the condition 136a091d823SDavid Xu * variable structure: 137a091d823SDavid Xu */ 138a091d823SDavid Xu free(cv); 139a091d823SDavid Xu } 140a091d823SDavid Xu /* Return the completion status: */ 141a091d823SDavid Xu return (rval); 142a091d823SDavid Xu } 143a091d823SDavid Xu 144a091d823SDavid Xu struct cond_cancel_info 145a091d823SDavid Xu { 146a091d823SDavid Xu pthread_mutex_t *mutex; 147a091d823SDavid Xu pthread_cond_t *cond; 148a9794459SDavid Xu int count; 149a091d823SDavid Xu }; 150a091d823SDavid Xu 151a091d823SDavid Xu static void 152a091d823SDavid Xu cond_cancel_handler(void *arg) 153a091d823SDavid Xu { 154a091d823SDavid Xu struct pthread *curthread = _get_curthread(); 155a9794459SDavid Xu struct cond_cancel_info *info = (struct cond_cancel_info *)arg; 156a091d823SDavid Xu pthread_cond_t cv; 157a091d823SDavid Xu 158347126a2SDavid Xu if (info->cond != NULL) { 159a9794459SDavid Xu cv = *(info->cond); 1602bd2c907SDavid Xu THR_UMUTEX_UNLOCK(curthread, &cv->c_lock); 161347126a2SDavid Xu } 162a9794459SDavid Xu _mutex_cv_lock(info->mutex, info->count); 163a091d823SDavid Xu } 164a091d823SDavid Xu 165*635f917aSDavid Xu /* 166*635f917aSDavid Xu * Cancellation behaivor: 167*635f917aSDavid Xu * Thread may be canceled at start, if thread is canceled, it means it 168*635f917aSDavid Xu * did not get a wakeup from pthread_cond_signal(), otherwise, it is 169*635f917aSDavid Xu * not canceled. 170*635f917aSDavid Xu * Thread cancellation never cause wakeup from pthread_cond_signal() 171*635f917aSDavid Xu * to be lost. 172*635f917aSDavid Xu */ 173a091d823SDavid Xu static int 174a091d823SDavid Xu cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 175a091d823SDavid Xu const struct timespec *abstime, int cancel) 176a091d823SDavid Xu { 177a091d823SDavid Xu struct pthread *curthread = _get_curthread(); 178a091d823SDavid Xu struct timespec ts, ts2, *tsp; 179a9794459SDavid Xu struct cond_cancel_info info; 180a091d823SDavid Xu pthread_cond_t cv; 181a091d823SDavid Xu int ret = 0; 182a091d823SDavid Xu 183a091d823SDavid Xu /* 184a091d823SDavid Xu * If the condition variable is statically initialized, 185a091d823SDavid Xu * perform the dynamic initialization: 186a091d823SDavid Xu */ 187a091d823SDavid Xu if (__predict_false(*cond == NULL && 188a091d823SDavid Xu (ret = init_static(curthread, cond)) != 0)) 189a091d823SDavid Xu return (ret); 190a091d823SDavid Xu 191*635f917aSDavid Xu _thr_testcancel(curthread); 192*635f917aSDavid Xu 193a091d823SDavid Xu cv = *cond; 1942bd2c907SDavid Xu THR_UMUTEX_LOCK(curthread, &cv->c_lock); 195a9794459SDavid Xu ret = _mutex_cv_unlock(mutex, &info.count); 196a091d823SDavid Xu if (ret) { 1972bd2c907SDavid Xu THR_UMUTEX_UNLOCK(curthread, &cv->c_lock); 198a091d823SDavid Xu return (ret); 199a091d823SDavid Xu } 2002bd2c907SDavid Xu 201a9794459SDavid Xu info.mutex = mutex; 202a9794459SDavid Xu info.cond = cond; 203a091d823SDavid Xu 204a091d823SDavid Xu if (abstime != NULL) { 205a091d823SDavid Xu clock_gettime(cv->c_clockid, &ts); 206a091d823SDavid Xu TIMESPEC_SUB(&ts2, abstime, &ts); 207a091d823SDavid Xu tsp = &ts2; 208a091d823SDavid Xu } else 209a091d823SDavid Xu tsp = NULL; 210a091d823SDavid Xu 211a091d823SDavid Xu if (cancel) { 212a9794459SDavid Xu THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info); 213*635f917aSDavid Xu _thr_cancel_enter_defer(curthread, 0); 2142bd2c907SDavid Xu ret = _thr_ucond_wait(&cv->c_kerncv, &cv->c_lock, tsp, 1); 215347126a2SDavid Xu info.cond = NULL; 216*635f917aSDavid Xu _thr_cancel_leave_defer(curthread, (ret != 0)); 217a091d823SDavid Xu THR_CLEANUP_POP(curthread, 0); 218a091d823SDavid Xu } else { 2192bd2c907SDavid Xu ret = _thr_ucond_wait(&cv->c_kerncv, &cv->c_lock, tsp, 0); 220a091d823SDavid Xu } 2212bd2c907SDavid Xu if (ret == EINTR) 222a091d823SDavid Xu ret = 0; 223a9794459SDavid Xu _mutex_cv_lock(mutex, info.count); 224a091d823SDavid Xu return (ret); 225bb535300SJeff Roberson } 226bb535300SJeff Roberson 227bb535300SJeff Roberson int 228bb535300SJeff Roberson _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 229bb535300SJeff Roberson { 230bb535300SJeff Roberson 231a091d823SDavid Xu return (cond_wait_common(cond, mutex, NULL, 0)); 232a091d823SDavid Xu } 233bb535300SJeff Roberson 234a091d823SDavid Xu int 235a091d823SDavid Xu __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 236a091d823SDavid Xu { 237bb535300SJeff Roberson 238a091d823SDavid Xu return (cond_wait_common(cond, mutex, NULL, 1)); 239bb535300SJeff Roberson } 240bb535300SJeff Roberson 241bb535300SJeff Roberson int 242bb535300SJeff Roberson _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 243bb535300SJeff Roberson const struct timespec * abstime) 244bb535300SJeff Roberson { 245a091d823SDavid Xu 246a091d823SDavid Xu if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 247a091d823SDavid Xu abstime->tv_nsec >= 1000000000) 248dd3b229eSMike Makonnen return (EINVAL); 249dd3b229eSMike Makonnen 250a091d823SDavid Xu return (cond_wait_common(cond, mutex, abstime, 0)); 251a091d823SDavid Xu } 252a091d823SDavid Xu 253a091d823SDavid Xu int 254a091d823SDavid Xu __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 255a091d823SDavid Xu const struct timespec *abstime) 256a091d823SDavid Xu { 257a091d823SDavid Xu 258a091d823SDavid Xu if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 259a091d823SDavid Xu abstime->tv_nsec >= 1000000000) 260a091d823SDavid Xu return (EINVAL); 261a091d823SDavid Xu 262a091d823SDavid Xu return (cond_wait_common(cond, mutex, abstime, 1)); 263dd3b229eSMike Makonnen } 264dd3b229eSMike Makonnen 265dd3b229eSMike Makonnen static int 266a091d823SDavid Xu cond_signal_common(pthread_cond_t *cond, int broadcast) 267dd3b229eSMike Makonnen { 268a091d823SDavid Xu struct pthread *curthread = _get_curthread(); 269a091d823SDavid Xu pthread_cond_t cv; 2702bd2c907SDavid Xu int ret = 0; 271bb535300SJeff Roberson 272bb535300SJeff Roberson /* 273bb535300SJeff Roberson * If the condition variable is statically initialized, perform dynamic 274bb535300SJeff Roberson * initialization. 275bb535300SJeff Roberson */ 276a091d823SDavid Xu if (__predict_false(*cond == NULL && 277a091d823SDavid Xu (ret = init_static(curthread, cond)) != 0)) 278a091d823SDavid Xu return (ret); 279bb535300SJeff Roberson 280a091d823SDavid Xu cv = *cond; 2812bd2c907SDavid Xu THR_UMUTEX_LOCK(curthread, &cv->c_lock); 2822bd2c907SDavid Xu if (!broadcast) 2832bd2c907SDavid Xu ret = _thr_ucond_signal(&cv->c_kerncv); 2842bd2c907SDavid Xu else 2852bd2c907SDavid Xu ret = _thr_ucond_broadcast(&cv->c_kerncv); 2862bd2c907SDavid Xu THR_UMUTEX_UNLOCK(curthread, &cv->c_lock); 287a091d823SDavid Xu return (ret); 288bb535300SJeff Roberson } 289bb535300SJeff Roberson 290bb535300SJeff Roberson int 291bb535300SJeff Roberson _pthread_cond_signal(pthread_cond_t * cond) 292bb535300SJeff Roberson { 293a091d823SDavid Xu 294a091d823SDavid Xu return (cond_signal_common(cond, 0)); 295bb535300SJeff Roberson } 296bb535300SJeff Roberson 297bb535300SJeff Roberson int 298bb535300SJeff Roberson _pthread_cond_broadcast(pthread_cond_t * cond) 299bb535300SJeff Roberson { 300a091d823SDavid Xu 301a091d823SDavid Xu return (cond_signal_common(cond, 1)); 302a224a391SMike Makonnen } 303