1 /*
2  * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice unmodified, this list of conditions, and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * $DragonFly: src/lib/libthread_xu/thread/thr_cond.c,v 1.5 2005/04/05 23:04:22 davidxu Exp $
27  */
28 
29 #include <machine/tls.h>
30 
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <pthread.h>
35 #include <limits.h>
36 
37 #include "thr_private.h"
38 
39 /*
40  * Prototypes
41  */
42 static int cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
43 static int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
44 		    const struct timespec *abstime, int cancel);
45 static int cond_signal_common(pthread_cond_t *cond, int broadcast);
46 
47 /*
48  * Double underscore versions are cancellation points.  Single underscore
49  * versions are not and are provided for libc internal usage (which
50  * shouldn't introduce cancellation points).
51  */
52 __weak_reference(__pthread_cond_wait, pthread_cond_wait);
53 __weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait);
54 
55 __weak_reference(_pthread_cond_init, pthread_cond_init);
56 __weak_reference(_pthread_cond_destroy, pthread_cond_destroy);
57 __weak_reference(_pthread_cond_signal, pthread_cond_signal);
58 __weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
59 
60 static int
61 cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
62 {
63 	pthread_cond_t	pcond;
64 	int             rval = 0;
65 
66 	if ((pcond = (pthread_cond_t)
67 	    malloc(sizeof(struct pthread_cond))) == NULL) {
68 		rval = ENOMEM;
69 	} else {
70 		/*
71 		 * Initialise the condition variable structure:
72 		 */
73 		_thr_umtx_init(&pcond->c_lock);
74 		pcond->c_seqno = 0;
75 		pcond->c_waiters = 0;
76 		pcond->c_wakeups = 0;
77 		if (cond_attr == NULL || *cond_attr == NULL) {
78 			pcond->c_pshared = 0;
79 			pcond->c_clockid = CLOCK_REALTIME;
80 		} else {
81 			pcond->c_pshared = (*cond_attr)->c_pshared;
82 			pcond->c_clockid = (*cond_attr)->c_clockid;
83 		}
84 		*cond = pcond;
85 	}
86 	/* Return the completion status: */
87 	return (rval);
88 }
89 
90 static int
91 init_static(struct pthread *thread, pthread_cond_t *cond)
92 {
93 	int ret;
94 
95 	THR_LOCK_ACQUIRE(thread, &_cond_static_lock);
96 
97 	if (*cond == NULL)
98 		ret = cond_init(cond, NULL);
99 	else
100 		ret = 0;
101 
102 	THR_LOCK_RELEASE(thread, &_cond_static_lock);
103 
104 	return (ret);
105 }
106 
107 int
108 _pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
109 {
110 	*cond = NULL;
111 	return cond_init(cond, cond_attr);
112 }
113 
114 int
115 _pthread_cond_destroy(pthread_cond_t *cond)
116 {
117 	struct pthread_cond	*cv;
118 	struct pthread		*curthread = tls_get_curthread();
119 	int			rval = 0;
120 
121 	if (*cond == NULL)
122 		rval = EINVAL;
123 	else {
124 		/* Lock the condition variable structure: */
125 		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
126 		if ((*cond)->c_waiters + (*cond)->c_wakeups != 0) {
127 			THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
128 			return (EBUSY);
129 		}
130 
131 		/*
132 		 * NULL the caller's pointer now that the condition
133 		 * variable has been destroyed:
134 		 */
135 		cv = *cond;
136 		*cond = NULL;
137 
138 		/* Unlock the condition variable structure: */
139 		THR_LOCK_RELEASE(curthread, &cv->c_lock);
140 
141 		/* Free the cond lock structure: */
142 
143 		/*
144 		 * Free the memory allocated for the condition
145 		 * variable structure:
146 		 */
147 		free(cv);
148 
149 	}
150 	/* Return the completion status: */
151 	return (rval);
152 }
153 
154 struct cond_cancel_info
155 {
156 	pthread_mutex_t	*mutex;
157 	pthread_cond_t	*cond;
158 	long		seqno;
159 };
160 
161 static void
162 cond_cancel_handler(void *arg)
163 {
164 	struct pthread *curthread = tls_get_curthread();
165 	struct cond_cancel_info *cci = (struct cond_cancel_info *)arg;
166 	pthread_cond_t cv;
167 
168 	cv = *(cci->cond);
169 	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
170 	if (cv->c_seqno != cci->seqno && cv->c_wakeups != 0) {
171 		if (cv->c_waiters > 0) {
172 			cv->c_seqno++;
173 			_thr_umtx_wake(&cv->c_seqno, 1);
174 		} else
175 			cv->c_wakeups--;
176 	} else {
177 		cv->c_waiters--;
178 	}
179 	THR_LOCK_RELEASE(curthread, &cv->c_lock);
180 
181 	_mutex_cv_lock(cci->mutex);
182 }
183 
184 static int
185 cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
186 	const struct timespec *abstime, int cancel)
187 {
188 	struct pthread	*curthread = tls_get_curthread();
189 	struct timespec ts, ts2, *tsp;
190 	struct cond_cancel_info cci;
191 	pthread_cond_t  cv;
192 	long		seq, oldseq;
193 	int		oldcancel;
194 	int		ret = 0;
195 
196 	/*
197 	 * If the condition variable is statically initialized,
198 	 * perform the dynamic initialization:
199 	 */
200 	if (__predict_false(*cond == NULL &&
201 	    (ret = init_static(curthread, cond)) != 0))
202 		return (ret);
203 
204 	cv = *cond;
205 	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
206 	ret = _mutex_cv_unlock(mutex);
207 	if (ret) {
208 		THR_LOCK_RELEASE(curthread, &cv->c_lock);
209 		return (ret);
210 	}
211 	oldseq = seq = cv->c_seqno;
212 	cci.mutex = mutex;
213 	cci.cond  = cond;
214 	cci.seqno = oldseq;
215 
216 	cv->c_waiters++;
217 	do {
218 		THR_LOCK_RELEASE(curthread, &cv->c_lock);
219 
220 		if (abstime != NULL) {
221 			clock_gettime(cv->c_clockid, &ts);
222 			TIMESPEC_SUB(&ts2, abstime, &ts);
223 			tsp = &ts2;
224 		} else
225 			tsp = NULL;
226 
227 		if (cancel) {
228 			THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &cci);
229 			oldcancel = _thr_cancel_enter(curthread);
230 			ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp,
231 				cv->c_clockid);
232 			_thr_cancel_leave(curthread, oldcancel);
233 			THR_CLEANUP_POP(curthread, 0);
234 		} else {
235 			ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp,
236 				cv->c_clockid);
237 		}
238 
239 		THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
240 		seq = cv->c_seqno;
241 		if (abstime != NULL && ret == ETIMEDOUT)
242 			break;
243 
244 		/*
245 		 * loop if we have never been told to wake up
246 		 * or we lost a race.
247 		 */
248 	} while (seq == oldseq || cv->c_wakeups == 0);
249 
250 	if (seq != oldseq && cv->c_wakeups != 0) {
251 		cv->c_wakeups--;
252 		ret = 0;
253 	} else {
254 		cv->c_waiters--;
255 	}
256 	THR_LOCK_RELEASE(curthread, &cv->c_lock);
257 	_mutex_cv_lock(mutex);
258 	return (ret);
259 }
260 
261 int
262 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
263 {
264 
265 	return (cond_wait_common(cond, mutex, NULL, 0));
266 }
267 
268 int
269 __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
270 {
271 
272 	return (cond_wait_common(cond, mutex, NULL, 1));
273 }
274 
275 int
276 _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
277 		       const struct timespec * abstime)
278 {
279 	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
280 	    abstime->tv_nsec >= 1000000000)
281 		return (EINVAL);
282 
283 	return (cond_wait_common(cond, mutex, abstime, 0));
284 }
285 
286 int
287 __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
288 		       const struct timespec *abstime)
289 {
290 	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
291 	    abstime->tv_nsec >= 1000000000)
292 		return (EINVAL);
293 
294 	return (cond_wait_common(cond, mutex, abstime, 1));
295 }
296 
297 static int
298 cond_signal_common(pthread_cond_t *cond, int broadcast)
299 {
300 	struct pthread	*curthread = tls_get_curthread();
301 	pthread_cond_t	cv;
302 	int		ret = 0;
303 
304 	/*
305 	 * If the condition variable is statically initialized, perform dynamic
306 	 * initialization.
307 	 */
308 	if (__predict_false(*cond == NULL &&
309 	    (ret = init_static(curthread, cond)) != 0))
310 		return (ret);
311 
312 	cv = *cond;
313 	/* Lock the condition variable structure. */
314 	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
315 	if (cv->c_waiters) {
316 		if (!broadcast) {
317 			cv->c_wakeups++;
318 			cv->c_waiters--;
319 			cv->c_seqno++;
320 			_thr_umtx_wake(&cv->c_seqno, 1);
321 		} else {
322 			cv->c_wakeups += cv->c_waiters;
323 			cv->c_waiters = 0;
324 			cv->c_seqno++;
325 			_thr_umtx_wake(&cv->c_seqno, INT_MAX);
326 		}
327 	}
328 	THR_LOCK_RELEASE(curthread, &cv->c_lock);
329 	return (ret);
330 }
331 
332 int
333 _pthread_cond_signal(pthread_cond_t * cond)
334 {
335 
336 	return (cond_signal_common(cond, 0));
337 }
338 
339 int
340 _pthread_cond_broadcast(pthread_cond_t * cond)
341 {
342 
343 	return (cond_signal_common(cond, 1));
344 }
345