xref: /dflybsd-src/lib/libthread_xu/thread/thr_cond.c (revision c6cf4f8f1ebc9e3fe2a8c566f08adfc86122c7bf)
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.2 2005/02/26 02:04:22 davidxu Exp $
27  */
28 
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <pthread.h>
33 #include <limits.h>
34 
35 #include "thr_private.h"
36 
37 /*
38  * Prototypes
39  */
40 static int cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
41 static int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
42 		    const struct timespec *abstime, int cancel);
43 static int cond_signal_common(pthread_cond_t *cond, int broadcast);
44 
45 /*
46  * Double underscore versions are cancellation points.  Single underscore
47  * versions are not and are provided for libc internal usage (which
48  * shouldn't introduce cancellation points).
49  */
50 __weak_reference(__pthread_cond_wait, pthread_cond_wait);
51 __weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait);
52 
53 __weak_reference(_pthread_cond_init, pthread_cond_init);
54 __weak_reference(_pthread_cond_destroy, pthread_cond_destroy);
55 __weak_reference(_pthread_cond_signal, pthread_cond_signal);
56 __weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
57 
58 static int
59 cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
60 {
61 	pthread_cond_t	pcond;
62 	int             rval = 0;
63 
64 	if ((pcond = (pthread_cond_t)
65 	    malloc(sizeof(struct pthread_cond))) == NULL) {
66 		rval = ENOMEM;
67 	} else {
68 		/*
69 		 * Initialise the condition variable structure:
70 		 */
71 		_thr_umtx_init(&pcond->c_lock);
72 		pcond->c_seqno = 0;
73 		pcond->c_waiters = 0;
74 		pcond->c_wakeups = 0;
75 		if (cond_attr == NULL || *cond_attr == NULL) {
76 			pcond->c_pshared = 0;
77 			pcond->c_clockid = CLOCK_REALTIME;
78 		} else {
79 			pcond->c_pshared = (*cond_attr)->c_pshared;
80 			pcond->c_clockid = (*cond_attr)->c_clockid;
81 		}
82 		*cond = pcond;
83 	}
84 	/* Return the completion status: */
85 	return (rval);
86 }
87 
88 static int
89 init_static(struct pthread *thread, pthread_cond_t *cond)
90 {
91 	int ret;
92 
93 	THR_LOCK_ACQUIRE(thread, &_cond_static_lock);
94 
95 	if (*cond == NULL)
96 		ret = cond_init(cond, NULL);
97 	else
98 		ret = 0;
99 
100 	THR_LOCK_RELEASE(thread, &_cond_static_lock);
101 
102 	return (ret);
103 }
104 
105 int
106 _pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
107 {
108 	*cond = NULL;
109 	return cond_init(cond, cond_attr);
110 }
111 
112 int
113 _pthread_cond_destroy(pthread_cond_t *cond)
114 {
115 	struct pthread_cond	*cv;
116 	struct pthread		*curthread = _get_curthread();
117 	int			rval = 0;
118 
119 	if (*cond == NULL)
120 		rval = EINVAL;
121 	else {
122 		/* Lock the condition variable structure: */
123 		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
124 		if ((*cond)->c_waiters + (*cond)->c_wakeups != 0) {
125 			THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
126 			return (EBUSY);
127 		}
128 
129 		/*
130 		 * NULL the caller's pointer now that the condition
131 		 * variable has been destroyed:
132 		 */
133 		cv = *cond;
134 		*cond = NULL;
135 
136 		/* Unlock the condition variable structure: */
137 		THR_LOCK_RELEASE(curthread, &cv->c_lock);
138 
139 		/* Free the cond lock structure: */
140 
141 		/*
142 		 * Free the memory allocated for the condition
143 		 * variable structure:
144 		 */
145 		free(cv);
146 
147 	}
148 	/* Return the completion status: */
149 	return (rval);
150 }
151 
152 struct cond_cancel_info
153 {
154 	pthread_mutex_t	*mutex;
155 	pthread_cond_t	*cond;
156 	long		seqno;
157 };
158 
159 static void
160 cond_cancel_handler(void *arg)
161 {
162 	struct pthread *curthread = _get_curthread();
163 	struct cond_cancel_info *cci = (struct cond_cancel_info *)arg;
164 	pthread_cond_t cv;
165 
166 	cv = *(cci->cond);
167 	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
168 	if (cv->c_seqno != cci->seqno && cv->c_wakeups != 0) {
169 		if (cv->c_waiters > 0) {
170 			cv->c_seqno++;
171 			_thr_umtx_wake(&cv->c_seqno, 1);
172 		} else
173 			cv->c_wakeups--;
174 	} else {
175 		cv->c_waiters--;
176 	}
177 	THR_LOCK_RELEASE(curthread, &cv->c_lock);
178 
179 	_mutex_cv_lock(cci->mutex);
180 }
181 
182 static int
183 cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
184 	const struct timespec *abstime, int cancel)
185 {
186 	struct pthread	*curthread = _get_curthread();
187 	struct timespec ts, ts2, *tsp;
188 	struct cond_cancel_info cci;
189 	pthread_cond_t  cv;
190 	long		seq, oldseq;
191 	int		oldcancel;
192 	int		ret = 0;
193 
194 	/*
195 	 * If the condition variable is statically initialized,
196 	 * perform the dynamic initialization:
197 	 */
198 	if (__predict_false(*cond == NULL &&
199 	    (ret = init_static(curthread, cond)) != 0))
200 		return (ret);
201 
202 	cv = *cond;
203 	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
204 	ret = _mutex_cv_unlock(mutex);
205 	if (ret) {
206 		THR_LOCK_RELEASE(curthread, &cv->c_lock);
207 		return (ret);
208 	}
209 	oldseq = seq = cv->c_seqno;
210 	cci.mutex = mutex;
211 	cci.cond  = cond;
212 	cci.seqno = oldseq;
213 
214 	cv->c_waiters++;
215 	do {
216 		THR_LOCK_RELEASE(curthread, &cv->c_lock);
217 
218 		if (abstime != NULL) {
219 			clock_gettime(cv->c_clockid, &ts);
220 			TIMESPEC_SUB(&ts2, abstime, &ts);
221 			tsp = &ts2;
222 		} else
223 			tsp = NULL;
224 
225 		if (cancel) {
226 			THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &cci);
227 			oldcancel = _thr_cancel_enter(curthread);
228 			ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp);
229 			_thr_cancel_leave(curthread, oldcancel);
230 			THR_CLEANUP_POP(curthread, 0);
231 		} else {
232 			ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp);
233 		}
234 
235 		THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
236 		seq = cv->c_seqno;
237 		if (abstime != NULL && ret == ETIMEDOUT)
238 			break;
239 
240 		/*
241 		 * loop if we have never been told to wake up
242 		 * or we lost a race.
243 		 */
244 	} while (seq == oldseq || cv->c_wakeups == 0);
245 
246 	if (seq != oldseq && cv->c_wakeups != 0) {
247 		cv->c_wakeups--;
248 		ret = 0;
249 	} else {
250 		cv->c_waiters--;
251 	}
252 	THR_LOCK_RELEASE(curthread, &cv->c_lock);
253 	_mutex_cv_lock(mutex);
254 	return (ret);
255 }
256 
257 int
258 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
259 {
260 
261 	return (cond_wait_common(cond, mutex, NULL, 0));
262 }
263 
264 __strong_reference(_pthread_cond_wait, _thr_cond_wait);
265 
266 int
267 __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
268 {
269 
270 	return (cond_wait_common(cond, mutex, NULL, 1));
271 }
272 
273 int
274 _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
275 		       const struct timespec * abstime)
276 {
277 	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
278 	    abstime->tv_nsec >= 1000000000)
279 		return (EINVAL);
280 
281 	return (cond_wait_common(cond, mutex, abstime, 0));
282 }
283 
284 __strong_reference(_pthread_cond_timedwait, _thr_cond_timedwait);
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 = _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 __strong_reference(_pthread_cond_signal, _thr_cond_signal);
340 
341 int
342 _pthread_cond_broadcast(pthread_cond_t * cond)
343 {
344 
345 	return (cond_signal_common(cond, 1));
346 }
347 
348 __strong_reference(_pthread_cond_broadcast, _thr_cond_broadcast);
349