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  */
27 
28 #include "namespace.h"
29 #include <machine/tls.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <pthread.h>
33 #include <limits.h>
34 #include "un-namespace.h"
35 
36 #include "thr_private.h"
37 
38 #ifdef _PTHREADS_DEBUGGING
39 #include <stdio.h>
40 #include <stdarg.h>
41 #include <sys/file.h>
42 #endif
43 
44 #define cpu_ccfence()	__asm __volatile("" : : : "memory")
45 
46 umtx_t		_cond_static_lock;
47 
48 #ifdef _PTHREADS_DEBUGGING
49 
50 static
51 void
52 cond_log(const char *ctl, ...)
53 {
54 	char buf[256];
55 	va_list va;
56 	size_t len;
57 
58 	va_start(va, ctl);
59 	len = vsnprintf(buf, sizeof(buf), ctl, va);
60 	va_end(va);
61 	_thr_log(buf, len);
62 }
63 
64 #else
65 
66 static __inline
67 void
68 cond_log(const char *ctl __unused, ...)
69 {
70 }
71 
72 #endif
73 
74 /*
75  * Prototypes
76  */
77 int	__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
78 int	__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
79 				 const struct timespec *abstime);
80 static int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
81 			    const struct timespec *abstime, int cancel);
82 static int cond_signal_common(pthread_cond_t *cond, int broadcast);
83 
84 static int
85 cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
86 {
87 	pthread_cond_t pcond;
88 	int rval = 0;
89 
90 	if ((pcond = (pthread_cond_t)
91 	    malloc(sizeof(struct pthread_cond))) == NULL) {
92 		rval = ENOMEM;
93 	} else {
94 		/*
95 		 * Initialise the condition variable structure:
96 		 */
97 		_thr_umtx_init(&pcond->c_lock);
98 		if (cond_attr == NULL || *cond_attr == NULL) {
99 			pcond->c_pshared = 0;
100 			pcond->c_clockid = CLOCK_REALTIME;
101 		} else {
102 			pcond->c_pshared = (*cond_attr)->c_pshared;
103 			pcond->c_clockid = (*cond_attr)->c_clockid;
104 		}
105 		TAILQ_INIT(&pcond->c_waitlist);
106 		*cond = pcond;
107 	}
108 	/* Return the completion status: */
109 	return (rval);
110 }
111 
112 #if 0
113 void
114 _cond_reinit(pthread_cond_t cond)
115 {
116 	if (cond) {
117 		_thr_umtx_init(&cond->c_lock);
118 #if 0
119 		/* retain state */
120 		cond->c_pshared = 0;
121 		cond->c_clockid = CLOCK_REALTIME;
122 #endif
123 		TAILQ_INIT(&cond->c_waitlist);
124 	}
125 }
126 #endif
127 
128 static int
129 init_static(struct pthread *thread, pthread_cond_t *cond)
130 {
131 	int ret;
132 
133 	THR_LOCK_ACQUIRE(thread, &_cond_static_lock);
134 
135 	if (*cond == NULL)
136 		ret = cond_init(cond, NULL);
137 	else
138 		ret = 0;
139 
140 	THR_LOCK_RELEASE(thread, &_cond_static_lock);
141 
142 	return (ret);
143 }
144 
145 int
146 _pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
147 {
148 	*cond = NULL;
149 	return cond_init(cond, cond_attr);
150 }
151 
152 int
153 _pthread_cond_destroy(pthread_cond_t *cond)
154 {
155 	struct pthread_cond	*cv;
156 	struct pthread		*curthread = tls_get_curthread();
157 	int			rval = 0;
158 
159 	if (cond == NULL)
160 		rval = EINVAL;
161 	else if (*cond == NULL)
162 		rval = 0;
163 	else {
164 		/* Lock the condition variable structure: */
165 		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
166 		if (TAILQ_FIRST(&(*cond)->c_waitlist)) {
167 			THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
168 			return (EBUSY);
169 		}
170 
171 		/*
172 		 * NULL the caller's pointer now that the condition
173 		 * variable has been destroyed:
174 		 */
175 		cv = *cond;
176 		*cond = NULL;
177 
178 		/* Unlock the condition variable structure: */
179 		THR_LOCK_RELEASE(curthread, &cv->c_lock);
180 
181 		/* Free the cond lock structure: */
182 
183 		/*
184 		 * Free the memory allocated for the condition
185 		 * variable structure:
186 		 */
187 		free(cv);
188 
189 	}
190 	/* Return the completion status: */
191 	return (rval);
192 }
193 
194 struct cond_cancel_info {
195 	TAILQ_ENTRY(cond_cancel_info) entry;
196 	pthread_mutex_t	*mutex;
197 	pthread_cond_t	*cond;
198 	int		count;
199 	int		queued;
200 };
201 
202 static void
203 cond_cancel_handler(void *arg)
204 {
205 	struct pthread *curthread = tls_get_curthread();
206 	struct cond_cancel_info *info = (struct cond_cancel_info *)arg;
207 	pthread_cond_t cv;
208 
209 	cv = *info->cond;
210 	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
211 	cond_log("cond_cancel %p\n", cv);
212 
213 	if (info->queued) {
214 		info->queued = 0;
215 		cond_log("cond_cancel %p: info %p\n", cv, info);
216 		TAILQ_REMOVE(&cv->c_waitlist, info, entry);
217 		_thr_umtx_wake(&info->queued, 0);
218 	}
219 	THR_LOCK_RELEASE(curthread, &cv->c_lock);
220 
221 	/* _mutex_cv_lock(info->mutex, info->count); */
222 }
223 
224 /*
225  * Wait for pthread_cond_t to be signaled.
226  *
227  * NOTE: EINTR is ignored and may not be returned by this function.
228  */
229 static int
230 cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
231 		 const struct timespec *abstime, int cancel)
232 {
233 	struct pthread	*curthread = tls_get_curthread();
234 	struct timespec ts, ts2, *tsp;
235 	struct cond_cancel_info info;
236 	pthread_cond_t  cv;
237 	int		oldcancel;
238 	int		ret;
239 
240 	/*
241 	 * If the condition variable is statically initialized,
242 	 * perform the dynamic initialization:
243 	 */
244 	cond_log("cond_wait_common %p on mutex %p info %p\n",
245 		*cond, *mutex, &info);
246 	if (__predict_false(*cond == NULL &&
247 	    (ret = init_static(curthread, cond)) != 0)) {
248 		cond_log("cond_wait_common %p (failedA %d)\n", *cond, ret);
249 		return (ret);
250 	}
251 
252 	cv = *cond;
253 	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
254 	ret = _mutex_cv_unlock(mutex, &info.count);
255 	if (ret) {
256 		cond_log("cond_wait_common %p (failedB %d)\n", cv, ret);
257 		THR_LOCK_RELEASE(curthread, &cv->c_lock);
258 		return ret;
259 	}
260 
261 	cpu_ccfence();
262 	info.mutex = mutex;
263 	info.cond  = cond;
264 	info.queued = 1;
265 	TAILQ_INSERT_TAIL(&cv->c_waitlist, &info, entry);
266 
267 	/*
268 	 * loop if we have never been told to wake up
269 	 * or we lost a race.
270 	 */
271 	while (info.queued) {
272 		THR_LOCK_RELEASE(curthread, &cv->c_lock);
273 
274 		if (abstime != NULL) {
275 			clock_gettime(cv->c_clockid, &ts);
276 			TIMESPEC_SUB(&ts2, abstime, &ts);
277 			tsp = &ts2;
278 		} else {
279 			tsp = NULL;
280 		}
281 
282 		if (cancel) {
283 			THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info);
284 			oldcancel = _thr_cancel_enter(curthread);
285 			ret = _thr_umtx_wait(&info.queued, 1, tsp,
286 					     cv->c_clockid);
287 			_thr_cancel_leave(curthread, oldcancel);
288 			THR_CLEANUP_POP(curthread, 0);
289 		} else {
290 			ret = _thr_umtx_wait(&info.queued, 1, tsp,
291 					     cv->c_clockid);
292 		}
293 
294 		/*
295 		 * Ignore EINTR.  Make sure ret is 0 if not ETIMEDOUT.
296 		 */
297 		THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
298 		if (abstime != NULL && ret == ETIMEDOUT)
299 			break;
300 		cpu_ccfence();
301 	}
302 
303 	if (info.queued) {
304 		info.queued = 0;
305 		TAILQ_REMOVE(&cv->c_waitlist, &info, entry);
306 		ret = ETIMEDOUT;
307 	} else {
308 		ret = 0;
309 	}
310 	THR_LOCK_RELEASE(curthread, &cv->c_lock);
311 
312 	cond_log("cond_wait_common %p (doneA)\n", cv);
313 	_mutex_cv_lock(mutex, info.count);
314 
315 	if (ret)
316 		cond_log("cond_wait_common %p (failed %d)\n", cv, ret);
317 	else
318 		cond_log("cond_wait_common %p (doneB)\n", cv);
319 
320 	return (ret);
321 }
322 
323 int
324 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
325 {
326 	return (cond_wait_common(cond, mutex, NULL, 0));
327 }
328 
329 int
330 __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
331 {
332 	return (cond_wait_common(cond, mutex, NULL, 1));
333 }
334 
335 int
336 _pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
337 		       const struct timespec * abstime)
338 {
339 	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
340 	    abstime->tv_nsec >= 1000000000)
341 		return (EINVAL);
342 
343 	return (cond_wait_common(cond, mutex, abstime, 0));
344 }
345 
346 int
347 __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
348 		       const struct timespec *abstime)
349 {
350 	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
351 	    abstime->tv_nsec >= 1000000000)
352 		return (EINVAL);
353 
354 	return (cond_wait_common(cond, mutex, abstime, 1));
355 }
356 
357 static int
358 cond_signal_common(pthread_cond_t *cond, int broadcast)
359 {
360 	struct pthread	*curthread = tls_get_curthread();
361 	struct cond_cancel_info *info;
362 	pthread_cond_t	cv;
363 	int		ret = 0;
364 
365 	cond_log("cond_signal_common %p broad=%d\n", *cond, broadcast);
366 
367 	/*
368 	 * If the condition variable is statically initialized, perform dynamic
369 	 * initialization.
370 	 */
371 	if (__predict_false(*cond == NULL &&
372 			    (ret = init_static(curthread, cond)) != 0)) {
373 		cond_log("cond_signal_common %p (failedA %d)\n", *cond, ret);
374 		return (ret);
375 	}
376 
377 	cv = *cond;
378 	/* Lock the condition variable structure. */
379 	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
380 	while ((info = TAILQ_FIRST(&cv->c_waitlist)) != NULL) {
381 		info->queued = 0;
382 		TAILQ_REMOVE(&cv->c_waitlist, info, entry);
383 		cond_log("cond_signal_common %p: wakeup %p\n", *cond, info);
384 		_thr_umtx_wake(&info->queued, 0);
385 		if (broadcast == 0)
386 			break;
387 	}
388 	THR_LOCK_RELEASE(curthread, &cv->c_lock);
389 
390 	if (ret)
391 		cond_log("cond_signal_common %p (failedB %d)\n", *cond, ret);
392 	else
393 		cond_log("cond_signal_common %p (done)\n", *cond);
394 
395 	return (ret);
396 }
397 
398 int
399 _pthread_cond_signal(pthread_cond_t * cond)
400 {
401 	return (cond_signal_common(cond, 0));
402 }
403 
404 int
405 _pthread_cond_broadcast(pthread_cond_t * cond)
406 {
407 	return (cond_signal_common(cond, 1));
408 }
409 
410 /*
411  * Double underscore versions are cancellation points.  Single underscore
412  * versions are not and are provided for libc internal usage (which
413  * shouldn't introduce cancellation points).
414  */
415 __strong_reference(__pthread_cond_wait, pthread_cond_wait);
416 __strong_reference(__pthread_cond_timedwait, pthread_cond_timedwait);
417 
418 __strong_reference(_pthread_cond_init, pthread_cond_init);
419 __strong_reference(_pthread_cond_destroy, pthread_cond_destroy);
420 __strong_reference(_pthread_cond_signal, pthread_cond_signal);
421 __strong_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
422