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 107*bbb64c21SDavid Xu #define CHECK_AND_INIT_COND \ 108*bbb64c21SDavid Xu if (__predict_false((cv = (*cond)) <= THR_COND_DESTROYED)) { \ 109*bbb64c21SDavid Xu if (cv == THR_COND_INITIALIZER) { \ 110*bbb64c21SDavid Xu int ret; \ 111*bbb64c21SDavid Xu ret = init_static(_get_curthread(), cond); \ 112*bbb64c21SDavid Xu if (ret) \ 113*bbb64c21SDavid Xu return (ret); \ 114*bbb64c21SDavid Xu } else if (cv == THR_COND_DESTROYED) { \ 115*bbb64c21SDavid Xu return (EINVAL); \ 116*bbb64c21SDavid Xu } \ 117*bbb64c21SDavid Xu cv = *cond; \ 118*bbb64c21SDavid Xu } 119*bbb64c21SDavid Xu 120bb535300SJeff Roberson int 121bb535300SJeff Roberson _pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) 122bb535300SJeff Roberson { 123bb535300SJeff Roberson 124a091d823SDavid Xu *cond = NULL; 125a091d823SDavid Xu return (cond_init(cond, cond_attr)); 126bb535300SJeff Roberson } 127bb535300SJeff Roberson 128bb535300SJeff Roberson int 129bb535300SJeff Roberson _pthread_cond_destroy(pthread_cond_t *cond) 130bb535300SJeff Roberson { 131a091d823SDavid Xu struct pthread *curthread = _get_curthread(); 1322bd2c907SDavid Xu struct pthread_cond *cv; 133a091d823SDavid Xu int rval = 0; 134bb535300SJeff Roberson 135*bbb64c21SDavid Xu if ((cv = *cond) == THR_COND_INITIALIZER) 136*bbb64c21SDavid Xu rval = 0; 137*bbb64c21SDavid Xu else if (cv == THR_COND_DESTROYED) 138a091d823SDavid Xu rval = EINVAL; 139a091d823SDavid Xu else { 1402bd2c907SDavid Xu cv = *cond; 1412bd2c907SDavid Xu THR_UMUTEX_LOCK(curthread, &cv->c_lock); 142*bbb64c21SDavid Xu *cond = THR_COND_DESTROYED; 1432bd2c907SDavid Xu THR_UMUTEX_UNLOCK(curthread, &cv->c_lock); 144a091d823SDavid Xu 145a091d823SDavid Xu /* 146a091d823SDavid Xu * Free the memory allocated for the condition 147a091d823SDavid Xu * variable structure: 148a091d823SDavid Xu */ 149a091d823SDavid Xu free(cv); 150a091d823SDavid Xu } 151a091d823SDavid Xu return (rval); 152a091d823SDavid Xu } 153a091d823SDavid Xu 154a091d823SDavid Xu struct cond_cancel_info 155a091d823SDavid Xu { 156a091d823SDavid Xu pthread_mutex_t *mutex; 157a091d823SDavid Xu pthread_cond_t *cond; 158a9794459SDavid Xu int count; 159a091d823SDavid Xu }; 160a091d823SDavid Xu 161a091d823SDavid Xu static void 162a091d823SDavid Xu cond_cancel_handler(void *arg) 163a091d823SDavid Xu { 164a091d823SDavid Xu struct pthread *curthread = _get_curthread(); 165a9794459SDavid Xu struct cond_cancel_info *info = (struct cond_cancel_info *)arg; 166a091d823SDavid Xu pthread_cond_t cv; 167a091d823SDavid Xu 168347126a2SDavid Xu if (info->cond != NULL) { 169a9794459SDavid Xu cv = *(info->cond); 1702bd2c907SDavid Xu THR_UMUTEX_UNLOCK(curthread, &cv->c_lock); 171347126a2SDavid Xu } 172a9794459SDavid Xu _mutex_cv_lock(info->mutex, info->count); 173a091d823SDavid Xu } 174a091d823SDavid Xu 175635f917aSDavid Xu /* 176635f917aSDavid Xu * Cancellation behaivor: 177635f917aSDavid Xu * Thread may be canceled at start, if thread is canceled, it means it 178635f917aSDavid Xu * did not get a wakeup from pthread_cond_signal(), otherwise, it is 179635f917aSDavid Xu * not canceled. 180635f917aSDavid Xu * Thread cancellation never cause wakeup from pthread_cond_signal() 181635f917aSDavid Xu * to be lost. 182635f917aSDavid Xu */ 183a091d823SDavid Xu static int 184a091d823SDavid Xu cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, 185a091d823SDavid Xu const struct timespec *abstime, int cancel) 186a091d823SDavid Xu { 187a091d823SDavid Xu struct pthread *curthread = _get_curthread(); 188a091d823SDavid Xu struct timespec ts, ts2, *tsp; 189a9794459SDavid Xu struct cond_cancel_info info; 190a091d823SDavid Xu pthread_cond_t cv; 191*bbb64c21SDavid Xu int ret; 192a091d823SDavid Xu 193a091d823SDavid Xu /* 194a091d823SDavid Xu * If the condition variable is statically initialized, 195a091d823SDavid Xu * perform the dynamic initialization: 196a091d823SDavid Xu */ 197*bbb64c21SDavid Xu CHECK_AND_INIT_COND 198635f917aSDavid Xu 199a091d823SDavid Xu cv = *cond; 2002bd2c907SDavid Xu THR_UMUTEX_LOCK(curthread, &cv->c_lock); 201a9794459SDavid Xu ret = _mutex_cv_unlock(mutex, &info.count); 202*bbb64c21SDavid Xu if (__predict_false(ret != 0)) { 2032bd2c907SDavid Xu THR_UMUTEX_UNLOCK(curthread, &cv->c_lock); 204a091d823SDavid Xu return (ret); 205a091d823SDavid Xu } 2062bd2c907SDavid Xu 207a9794459SDavid Xu info.mutex = mutex; 208a9794459SDavid Xu info.cond = cond; 209a091d823SDavid Xu 210a091d823SDavid Xu if (abstime != NULL) { 211a091d823SDavid Xu clock_gettime(cv->c_clockid, &ts); 212a091d823SDavid Xu TIMESPEC_SUB(&ts2, abstime, &ts); 213a091d823SDavid Xu tsp = &ts2; 214a091d823SDavid Xu } else 215a091d823SDavid Xu tsp = NULL; 216a091d823SDavid Xu 217a091d823SDavid Xu if (cancel) { 218a9794459SDavid Xu THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info); 21902c3c858SDavid Xu _thr_cancel_enter2(curthread, 0); 2202bd2c907SDavid Xu ret = _thr_ucond_wait(&cv->c_kerncv, &cv->c_lock, tsp, 1); 221347126a2SDavid Xu info.cond = NULL; 22202c3c858SDavid Xu _thr_cancel_leave(curthread, (ret != 0)); 223a091d823SDavid Xu THR_CLEANUP_POP(curthread, 0); 224a091d823SDavid Xu } else { 2252bd2c907SDavid Xu ret = _thr_ucond_wait(&cv->c_kerncv, &cv->c_lock, tsp, 0); 226a091d823SDavid Xu } 2272bd2c907SDavid Xu if (ret == EINTR) 228a091d823SDavid Xu ret = 0; 229a9794459SDavid Xu _mutex_cv_lock(mutex, info.count); 230a091d823SDavid Xu return (ret); 231bb535300SJeff Roberson } 232bb535300SJeff Roberson 233bb535300SJeff Roberson int 234bb535300SJeff Roberson _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 235bb535300SJeff Roberson { 236bb535300SJeff Roberson 237a091d823SDavid Xu return (cond_wait_common(cond, mutex, NULL, 0)); 238a091d823SDavid Xu } 239bb535300SJeff Roberson 240a091d823SDavid Xu int 241a091d823SDavid Xu __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 242a091d823SDavid Xu { 243bb535300SJeff Roberson 244a091d823SDavid Xu return (cond_wait_common(cond, mutex, NULL, 1)); 245bb535300SJeff Roberson } 246bb535300SJeff Roberson 247bb535300SJeff Roberson int 248bb535300SJeff Roberson _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, 249bb535300SJeff Roberson const struct timespec * abstime) 250bb535300SJeff Roberson { 251a091d823SDavid Xu 252a091d823SDavid Xu if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 253a091d823SDavid Xu abstime->tv_nsec >= 1000000000) 254dd3b229eSMike Makonnen return (EINVAL); 255dd3b229eSMike Makonnen 256a091d823SDavid Xu return (cond_wait_common(cond, mutex, abstime, 0)); 257a091d823SDavid Xu } 258a091d823SDavid Xu 259a091d823SDavid Xu int 260a091d823SDavid Xu __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 261a091d823SDavid Xu const struct timespec *abstime) 262a091d823SDavid Xu { 263a091d823SDavid Xu 264a091d823SDavid Xu if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || 265a091d823SDavid Xu abstime->tv_nsec >= 1000000000) 266a091d823SDavid Xu return (EINVAL); 267a091d823SDavid Xu 268a091d823SDavid Xu return (cond_wait_common(cond, mutex, abstime, 1)); 269dd3b229eSMike Makonnen } 270dd3b229eSMike Makonnen 271dd3b229eSMike Makonnen static int 272a091d823SDavid Xu cond_signal_common(pthread_cond_t *cond, int broadcast) 273dd3b229eSMike Makonnen { 274a091d823SDavid Xu struct pthread *curthread = _get_curthread(); 275a091d823SDavid Xu pthread_cond_t cv; 2762bd2c907SDavid Xu int ret = 0; 277bb535300SJeff Roberson 278bb535300SJeff Roberson /* 279bb535300SJeff Roberson * If the condition variable is statically initialized, perform dynamic 280bb535300SJeff Roberson * initialization. 281bb535300SJeff Roberson */ 282*bbb64c21SDavid Xu CHECK_AND_INIT_COND 283bb535300SJeff Roberson 2842bd2c907SDavid Xu THR_UMUTEX_LOCK(curthread, &cv->c_lock); 2852bd2c907SDavid Xu if (!broadcast) 2862bd2c907SDavid Xu ret = _thr_ucond_signal(&cv->c_kerncv); 2872bd2c907SDavid Xu else 2882bd2c907SDavid Xu ret = _thr_ucond_broadcast(&cv->c_kerncv); 2892bd2c907SDavid Xu THR_UMUTEX_UNLOCK(curthread, &cv->c_lock); 290a091d823SDavid Xu return (ret); 291bb535300SJeff Roberson } 292bb535300SJeff Roberson 293bb535300SJeff Roberson int 294bb535300SJeff Roberson _pthread_cond_signal(pthread_cond_t * cond) 295bb535300SJeff Roberson { 296a091d823SDavid Xu 297a091d823SDavid Xu return (cond_signal_common(cond, 0)); 298bb535300SJeff Roberson } 299bb535300SJeff Roberson 300bb535300SJeff Roberson int 301bb535300SJeff Roberson _pthread_cond_broadcast(pthread_cond_t * cond) 302bb535300SJeff Roberson { 303a091d823SDavid Xu 304a091d823SDavid Xu return (cond_signal_common(cond, 1)); 305a224a391SMike Makonnen } 306