1 /* $OpenBSD: rthread_cond.c,v 1.4 2017/09/05 02:40:54 guenther 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 <assert.h> 20 #include <errno.h> 21 #include <pthread.h> 22 #include <stdint.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <unistd.h> 26 27 #include "rthread.h" 28 #include "cancel.h" 29 #include "synch.h" 30 31 int 32 pthread_cond_init(pthread_cond_t *condp, const pthread_condattr_t *attr) 33 { 34 pthread_cond_t cond; 35 36 cond = calloc(1, sizeof(*cond)); 37 if (cond == NULL) 38 return (ENOMEM); 39 40 if (attr == NULL) 41 cond->clock = CLOCK_REALTIME; 42 else 43 cond->clock = (*attr)->ca_clock; 44 *condp = cond; 45 46 return (0); 47 } 48 DEF_STRONG(pthread_cond_init); 49 50 int 51 pthread_cond_destroy(pthread_cond_t *condp) 52 { 53 pthread_cond_t cond; 54 55 assert(condp != NULL); 56 cond = *condp; 57 58 if (cond != NULL) { 59 if (cond->mutex != NULL) { 60 #define MSG "pthread_cond_destroy on condvar with waiters!\n" 61 write(2, MSG, sizeof(MSG) - 1); 62 #undef MSG 63 return (EBUSY); 64 } 65 free(cond); 66 } 67 *condp = NULL; 68 69 return (0); 70 } 71 72 int 73 _rthread_cond_timedwait(pthread_cond_t cond, pthread_mutex_t *mutexp, 74 const struct timespec *abs) 75 { 76 struct pthread_mutex *mutex = (struct pthread_mutex *)*mutexp; 77 struct tib *tib = TIB_GET(); 78 pthread_t self = tib->tib_thread; 79 int error, rv = 0, canceled = 0, mutex_count = 0; 80 clockid_t clock = cond->clock; 81 int seq = cond->seq; 82 PREP_CANCEL_POINT(tib); 83 84 _rthread_debug(5, "%p: cond_timed %p,%p (%p)\n", self, 85 (void *)cond, (void *)mutex, (void *)mutex->owner); 86 87 ENTER_DELAYED_CANCEL_POINT(tib, self); 88 89 #if notyet 90 /* mark the condvar as being associated with this mutex */ 91 if (cond->mutex == NULL) 92 atomic_cas_ptr(&cond->mutex, NULL, mutex); 93 94 if (cond->mutex != mutex) { 95 LEAVE_CANCEL_POINT_INNER(tib, 1); 96 return (EINVAL); 97 } 98 #endif 99 100 /* snag the count in case this is a recursive mutex */ 101 if (mutex->type == PTHREAD_MUTEX_RECURSIVE) 102 mutex_count = mutex->count; 103 104 pthread_mutex_unlock(mutexp); 105 106 do { 107 /* If ``seq'' wraps you deserve to lose a signal. */ 108 error = _twait(&cond->seq, seq, clock, abs); 109 /* 110 * If we took a normal signal (not from cancellation) then 111 * we should just go back to sleep without changing state 112 * (timeouts, etc). 113 */ 114 } while ((error == EINTR) && 115 (tib->tib_canceled == 0 || (tib->tib_cantcancel & CANCEL_DISABLED))); 116 117 /* if timeout or canceled, make note of that */ 118 if (error == ETIMEDOUT) 119 rv = ETIMEDOUT; 120 else if (error == EINTR) 121 canceled = 1; 122 123 pthread_mutex_lock(mutexp); 124 125 /* restore the mutex's count */ 126 if (mutex->type == PTHREAD_MUTEX_RECURSIVE) 127 mutex->count = mutex_count; 128 129 LEAVE_CANCEL_POINT_INNER(tib, canceled); 130 131 return rv; 132 } 133 134 int 135 pthread_cond_timedwait(pthread_cond_t *condp, pthread_mutex_t *mutexp, 136 const struct timespec *abs) 137 { 138 pthread_cond_t cond; 139 int error; 140 141 if (*condp == NULL) { 142 if ((error = pthread_cond_init(condp, NULL))) 143 return (error); 144 } 145 146 cond = *condp; 147 if (abs == NULL || abs->tv_sec < 0 || abs->tv_nsec < 0 || 148 abs->tv_nsec >= 1000000000) 149 return (EINVAL); 150 151 return (_rthread_cond_timedwait(cond, mutexp, abs)); 152 } 153 154 int 155 pthread_cond_wait(pthread_cond_t *condp, pthread_mutex_t *mutexp) 156 { 157 pthread_cond_t cond; 158 int error; 159 160 if (*condp == NULL) { 161 if ((error = pthread_cond_init(condp, NULL))) 162 return (error); 163 } 164 165 cond = *condp; 166 return (_rthread_cond_timedwait(cond, mutexp, NULL)); 167 } 168 169 int 170 pthread_cond_signal(pthread_cond_t *condp) 171 { 172 pthread_cond_t cond; 173 int count; 174 175 if (*condp == NULL) 176 return (0); 177 178 cond = *condp; 179 180 atomic_inc_int(&cond->seq); 181 count = _wake(&cond->seq, 1); 182 183 _rthread_debug(5, "%p: cond_signal %p, %d awaken\n", pthread_self(), 184 (void *)cond, count); 185 186 return (0); 187 } 188 189 int 190 pthread_cond_broadcast(pthread_cond_t *condp) 191 { 192 pthread_cond_t cond; 193 int count; 194 195 if (*condp == NULL) 196 return (0); 197 198 cond = *condp; 199 200 atomic_inc_int(&cond->seq); 201 #if notyet 202 count = _requeue(&cond->seq, 1, INT_MAX, &cond->mutex->lock); 203 #else 204 count = _wake(&cond->seq, INT_MAX); 205 #endif 206 207 _rthread_debug(5, "%p: cond_broadcast %p, %d awaken\n", pthread_self(), 208 (void *)cond, count); 209 210 return (0); 211 } 212