| 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 |  |  | } |