xref: /dflybsd-src/lib/libthread_xu/thread/thr_cond.c (revision 23c32883e759b0ea42fdaff39e661bd1a12e3b9f)
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.3 2005/03/15 11:24:23 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 				cv->c_clockid);
230 			_thr_cancel_leave(curthread, oldcancel);
231 			THR_CLEANUP_POP(curthread, 0);
232 		} else {
233 			ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp,
234 				cv->c_clockid);
235 		}
236 
237 		THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
238 		seq = cv->c_seqno;
239 		if (abstime != NULL && ret == ETIMEDOUT)
240 			break;
241 
242 		/*
243 		 * loop if we have never been told to wake up
244 		 * or we lost a race.
245 		 */
246 	} while (seq == oldseq || cv->c_wakeups == 0);
247 
248 	if (seq != oldseq && cv->c_wakeups != 0) {
249 		cv->c_wakeups--;
250 		ret = 0;
251 	} else {
252 		cv->c_waiters--;
253 	}
254 	THR_LOCK_RELEASE(curthread, &cv->c_lock);
255 	_mutex_cv_lock(mutex);
256 	return (ret);
257 }
258 
259 int
260 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
261 {
262 
263 	return (cond_wait_common(cond, mutex, NULL, 0));
264 }
265 
266 __strong_reference(_pthread_cond_wait, _thr_cond_wait);
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 __strong_reference(_pthread_cond_timedwait, _thr_cond_timedwait);
287 
288 int
289 __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
290 		       const struct timespec *abstime)
291 {
292 	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
293 	    abstime->tv_nsec >= 1000000000)
294 		return (EINVAL);
295 
296 	return (cond_wait_common(cond, mutex, abstime, 1));
297 }
298 
299 static int
300 cond_signal_common(pthread_cond_t *cond, int broadcast)
301 {
302 	struct pthread	*curthread = _get_curthread();
303 	pthread_cond_t	cv;
304 	int		ret = 0;
305 
306 	/*
307 	 * If the condition variable is statically initialized, perform dynamic
308 	 * initialization.
309 	 */
310 	if (__predict_false(*cond == NULL &&
311 	    (ret = init_static(curthread, cond)) != 0))
312 		return (ret);
313 
314 	cv = *cond;
315 	/* Lock the condition variable structure. */
316 	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
317 	if (cv->c_waiters) {
318 		if (!broadcast) {
319 			cv->c_wakeups++;
320 			cv->c_waiters--;
321 			cv->c_seqno++;
322 			_thr_umtx_wake(&cv->c_seqno, 1);
323 		} else {
324 			cv->c_wakeups += cv->c_waiters;
325 			cv->c_waiters = 0;
326 			cv->c_seqno++;
327 			_thr_umtx_wake(&cv->c_seqno, INT_MAX);
328 		}
329 	}
330 	THR_LOCK_RELEASE(curthread, &cv->c_lock);
331 	return (ret);
332 }
333 
334 int
335 _pthread_cond_signal(pthread_cond_t * cond)
336 {
337 
338 	return (cond_signal_common(cond, 0));
339 }
340 
341 __strong_reference(_pthread_cond_signal, _thr_cond_signal);
342 
343 int
344 _pthread_cond_broadcast(pthread_cond_t * cond)
345 {
346 
347 	return (cond_signal_common(cond, 1));
348 }
349 
350 __strong_reference(_pthread_cond_broadcast, _thr_cond_broadcast);
351