1 /* $OpenBSD: rthread_cond.c,v 1.5 2019/01/29 17:40:26 mpi Exp $ */ 2 /* 3 * Copyright (c) 2017 Martin Pieuchot <mpi@openbsd.org> 4 * Copyright (c) 2012 Philip Guenther <guenther@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <errno.h> 20 #include <pthread.h> 21 #include <stdint.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <unistd.h> 25 26 #include "rthread.h" 27 #include "cancel.h" 28 #include "synch.h" 29 30 int 31 pthread_cond_init(pthread_cond_t *condp, const pthread_condattr_t *attr) 32 { 33 pthread_cond_t cond; 34 35 cond = calloc(1, sizeof(*cond)); 36 if (cond == NULL) 37 return (ENOMEM); 38 39 if (attr == NULL) 40 cond->clock = CLOCK_REALTIME; 41 else 42 cond->clock = (*attr)->ca_clock; 43 *condp = cond; 44 45 return (0); 46 } 47 DEF_STRONG(pthread_cond_init); 48 49 int 50 pthread_cond_destroy(pthread_cond_t *condp) 51 { 52 pthread_cond_t cond; 53 54 cond = *condp; 55 56 if (cond != NULL) { 57 if (cond->mutex != NULL) { 58 #define MSG "pthread_cond_destroy on condvar with waiters!\n" 59 write(2, MSG, sizeof(MSG) - 1); 60 #undef MSG 61 return (EBUSY); 62 } 63 free(cond); 64 } 65 *condp = NULL; 66 67 return (0); 68 } 69 70 int 71 _rthread_cond_timedwait(pthread_cond_t cond, pthread_mutex_t *mutexp, 72 const struct timespec *abs) 73 { 74 struct pthread_mutex *mutex = (struct pthread_mutex *)*mutexp; 75 struct tib *tib = TIB_GET(); 76 pthread_t self = tib->tib_thread; 77 int error, rv = 0, canceled = 0, mutex_count = 0; 78 clockid_t clock = cond->clock; 79 int seq = cond->seq; 80 PREP_CANCEL_POINT(tib); 81 82 _rthread_debug(5, "%p: cond_timed %p,%p (%p)\n", self, 83 (void *)cond, (void *)mutex, (void *)mutex->owner); 84 85 ENTER_DELAYED_CANCEL_POINT(tib, self); 86 87 #if notyet 88 /* mark the condvar as being associated with this mutex */ 89 if (cond->mutex == NULL) 90 atomic_cas_ptr(&cond->mutex, NULL, mutex); 91 92 if (cond->mutex != mutex) { 93 LEAVE_CANCEL_POINT_INNER(tib, 1); 94 return (EINVAL); 95 } 96 #endif 97 98 /* snag the count in case this is a recursive mutex */ 99 if (mutex->type == PTHREAD_MUTEX_RECURSIVE) 100 mutex_count = mutex->count; 101 102 pthread_mutex_unlock(mutexp); 103 104 do { 105 /* If ``seq'' wraps you deserve to lose a signal. */ 106 error = _twait(&cond->seq, seq, clock, abs); 107 /* 108 * If we took a normal signal (not from cancellation) then 109 * we should just go back to sleep without changing state 110 * (timeouts, etc). 111 */ 112 } while ((error == EINTR) && 113 (tib->tib_canceled == 0 || (tib->tib_cantcancel & CANCEL_DISABLED))); 114 115 /* if timeout or canceled, make note of that */ 116 if (error == ETIMEDOUT) 117 rv = ETIMEDOUT; 118 else if (error == EINTR) 119 canceled = 1; 120 121 pthread_mutex_lock(mutexp); 122 123 /* restore the mutex's count */ 124 if (mutex->type == PTHREAD_MUTEX_RECURSIVE) 125 mutex->count = mutex_count; 126 127 LEAVE_CANCEL_POINT_INNER(tib, canceled); 128 129 return rv; 130 } 131 132 int 133 pthread_cond_timedwait(pthread_cond_t *condp, pthread_mutex_t *mutexp, 134 const struct timespec *abs) 135 { 136 pthread_cond_t cond; 137 int error; 138 139 if (*condp == NULL) { 140 if ((error = pthread_cond_init(condp, NULL))) 141 return (error); 142 } 143 144 cond = *condp; 145 if (abs == NULL || abs->tv_sec < 0 || abs->tv_nsec < 0 || 146 abs->tv_nsec >= 1000000000) 147 return (EINVAL); 148 149 return (_rthread_cond_timedwait(cond, mutexp, abs)); 150 } 151 152 int 153 pthread_cond_wait(pthread_cond_t *condp, pthread_mutex_t *mutexp) 154 { 155 pthread_cond_t cond; 156 int error; 157 158 if (*condp == NULL) { 159 if ((error = pthread_cond_init(condp, NULL))) 160 return (error); 161 } 162 163 cond = *condp; 164 return (_rthread_cond_timedwait(cond, mutexp, NULL)); 165 } 166 167 int 168 pthread_cond_signal(pthread_cond_t *condp) 169 { 170 pthread_cond_t cond; 171 int count; 172 173 if (*condp == NULL) 174 return (0); 175 176 cond = *condp; 177 178 atomic_inc_int(&cond->seq); 179 count = _wake(&cond->seq, 1); 180 181 _rthread_debug(5, "%p: cond_signal %p, %d awaken\n", pthread_self(), 182 (void *)cond, count); 183 184 return (0); 185 } 186 187 int 188 pthread_cond_broadcast(pthread_cond_t *condp) 189 { 190 pthread_cond_t cond; 191 int count; 192 193 if (*condp == NULL) 194 return (0); 195 196 cond = *condp; 197 198 atomic_inc_int(&cond->seq); 199 #if notyet 200 count = _requeue(&cond->seq, 1, INT_MAX, &cond->mutex->lock); 201 #else 202 count = _wake(&cond->seq, INT_MAX); 203 #endif 204 205 _rthread_debug(5, "%p: cond_broadcast %p, %d awaken\n", pthread_self(), 206 (void *)cond, count); 207 208 return (0); 209 } 210