thr_cond.c revision 164583
1311116Sdim/*
2311116Sdim * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
3353358Sdim * All rights reserved.
4353358Sdim *
5353358Sdim * Redistribution and use in source and binary forms, with or without
6311116Sdim * modification, are permitted provided that the following conditions
7311116Sdim * are met:
8311116Sdim * 1. Redistributions of source code must retain the above copyright
9311116Sdim *    notice unmodified, this list of conditions, and the following
10311116Sdim *    disclaimer.
11311116Sdim * 2. Redistributions in binary form must reproduce the above copyright
12311116Sdim *    notice, this list of conditions and the following disclaimer in the
13311116Sdim *    documentation and/or other materials provided with the distribution.
14311116Sdim *
15311116Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16311116Sdim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17311116Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18311116Sdim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19311116Sdim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20311116Sdim * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21311116Sdim * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22311116Sdim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23311116Sdim * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24311116Sdim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25311116Sdim *
26311116Sdim * $FreeBSD: head/lib/libthr/thread/thr_cond.c 164583 2006-11-24 09:57:38Z davidxu $
27311116Sdim */
28311116Sdim
29311116Sdim#include "namespace.h"
30311116Sdim#include <stdlib.h>
31311116Sdim#include <errno.h>
32311116Sdim#include <string.h>
33311116Sdim#include <pthread.h>
34311116Sdim#include <limits.h>
35311116Sdim#include "un-namespace.h"
36311116Sdim
37311116Sdim#include "thr_private.h"
38311116Sdim
39311116Sdim/*
40311116Sdim * Prototypes
41311116Sdim */
42311116Sdimint	__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
43311116Sdimint	__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
44311116Sdim		       const struct timespec * abstime);
45311116Sdimstatic int cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
46311116Sdimstatic int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
47311116Sdim		    const struct timespec *abstime, int cancel);
48311116Sdimstatic int cond_signal_common(pthread_cond_t *cond, int broadcast);
49311116Sdim
50311116Sdim/*
51311116Sdim * Double underscore versions are cancellation points.  Single underscore
52311116Sdim * versions are not and are provided for libc internal usage (which
53311116Sdim * shouldn't introduce cancellation points).
54311116Sdim */
55311116Sdim__weak_reference(__pthread_cond_wait, pthread_cond_wait);
56311116Sdim__weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait);
57311116Sdim
58311116Sdim__weak_reference(_pthread_cond_init, pthread_cond_init);
59311116Sdim__weak_reference(_pthread_cond_destroy, pthread_cond_destroy);
60311116Sdim__weak_reference(_pthread_cond_signal, pthread_cond_signal);
61311116Sdim__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
62311116Sdim
63311116Sdimstatic int
64311116Sdimcond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
65311116Sdim{
66311116Sdim	pthread_cond_t	pcond;
67311116Sdim	int             rval = 0;
68311116Sdim
69311116Sdim	if ((pcond = (pthread_cond_t)
70	    malloc(sizeof(struct pthread_cond))) == NULL) {
71		rval = ENOMEM;
72	} else {
73		/*
74		 * Initialise the condition variable structure:
75		 */
76		_thr_umutex_init(&pcond->c_lock);
77		pcond->c_seqno = 0;
78		pcond->c_waiters = 0;
79		pcond->c_wakeups = 0;
80		if (cond_attr == NULL || *cond_attr == NULL) {
81			pcond->c_pshared = 0;
82			pcond->c_clockid = CLOCK_REALTIME;
83		} else {
84			pcond->c_pshared = (*cond_attr)->c_pshared;
85			pcond->c_clockid = (*cond_attr)->c_clockid;
86		}
87		*cond = pcond;
88	}
89	/* Return the completion status: */
90	return (rval);
91}
92
93static int
94init_static(struct pthread *thread, pthread_cond_t *cond)
95{
96	int ret;
97
98	THR_LOCK_ACQUIRE(thread, &_cond_static_lock);
99
100	if (*cond == NULL)
101		ret = cond_init(cond, NULL);
102	else
103		ret = 0;
104
105	THR_LOCK_RELEASE(thread, &_cond_static_lock);
106
107	return (ret);
108}
109
110int
111_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
112{
113
114	*cond = NULL;
115	return (cond_init(cond, cond_attr));
116}
117
118int
119_pthread_cond_destroy(pthread_cond_t *cond)
120{
121	struct pthread_cond	*cv;
122	struct pthread		*curthread = _get_curthread();
123	int			rval = 0;
124
125	if (*cond == NULL)
126		rval = EINVAL;
127	else {
128		/* Lock the condition variable structure: */
129		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
130		if ((*cond)->c_waiters + (*cond)->c_wakeups != 0) {
131			THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
132			return (EBUSY);
133		}
134
135		/*
136		 * NULL the caller's pointer now that the condition
137		 * variable has been destroyed:
138		 */
139		cv = *cond;
140		*cond = NULL;
141
142		/* Unlock the condition variable structure: */
143		THR_LOCK_RELEASE(curthread, &cv->c_lock);
144
145		/* Free the cond lock structure: */
146
147		/*
148		 * Free the memory allocated for the condition
149		 * variable structure:
150		 */
151		free(cv);
152
153	}
154	/* Return the completion status: */
155	return (rval);
156}
157
158struct cond_cancel_info
159{
160	pthread_mutex_t	*mutex;
161	pthread_cond_t	*cond;
162	long		seqno;
163	int		count;
164};
165
166static void
167cond_cancel_handler(void *arg)
168{
169	struct pthread *curthread = _get_curthread();
170	struct cond_cancel_info *info = (struct cond_cancel_info *)arg;
171	pthread_cond_t cv;
172
173	cv = *(info->cond);
174	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
175	if (cv->c_seqno != info->seqno && cv->c_wakeups != 0) {
176		if (cv->c_waiters > 0) {
177			cv->c_seqno++;
178			_thr_umtx_wake(&cv->c_seqno, 1);
179		} else
180			cv->c_wakeups--;
181	} else {
182		cv->c_waiters--;
183	}
184	THR_LOCK_RELEASE(curthread, &cv->c_lock);
185
186	_mutex_cv_lock(info->mutex, info->count);
187}
188
189static int
190cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
191	const struct timespec *abstime, int cancel)
192{
193	struct pthread	*curthread = _get_curthread();
194	struct timespec ts, ts2, *tsp;
195	struct cond_cancel_info info;
196	pthread_cond_t  cv;
197	long		seq, oldseq;
198	int		ret = 0;
199
200	/*
201	 * If the condition variable is statically initialized,
202	 * perform the dynamic initialization:
203	 */
204	if (__predict_false(*cond == NULL &&
205	    (ret = init_static(curthread, cond)) != 0))
206		return (ret);
207
208	cv = *cond;
209	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
210	ret = _mutex_cv_unlock(mutex, &info.count);
211	if (ret) {
212		THR_LOCK_RELEASE(curthread, &cv->c_lock);
213		return (ret);
214	}
215	oldseq = seq = cv->c_seqno;
216	info.mutex = mutex;
217	info.cond  = cond;
218	info.seqno = oldseq;
219
220	cv->c_waiters++;
221	do {
222		THR_LOCK_RELEASE(curthread, &cv->c_lock);
223
224		if (abstime != NULL) {
225			clock_gettime(cv->c_clockid, &ts);
226			TIMESPEC_SUB(&ts2, abstime, &ts);
227			tsp = &ts2;
228		} else
229			tsp = NULL;
230
231		if (cancel) {
232			THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info);
233			_thr_cancel_enter(curthread);
234			ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp);
235			_thr_cancel_leave(curthread);
236			THR_CLEANUP_POP(curthread, 0);
237		} else {
238			ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp);
239		}
240
241		THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
242		seq = cv->c_seqno;
243		if (abstime != NULL && ret == ETIMEDOUT)
244			break;
245
246		/*
247		 * loop if we have never been told to wake up
248		 * or we lost a race.
249		 */
250	} while (seq == oldseq || cv->c_wakeups == 0);
251
252	if (seq != oldseq && cv->c_wakeups != 0) {
253		cv->c_wakeups--;
254		ret = 0;
255	} else {
256		cv->c_waiters--;
257	}
258	THR_LOCK_RELEASE(curthread, &cv->c_lock);
259	_mutex_cv_lock(mutex, info.count);
260	return (ret);
261}
262
263int
264_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
265{
266
267	return (cond_wait_common(cond, mutex, NULL, 0));
268}
269
270int
271__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
272{
273
274	return (cond_wait_common(cond, mutex, NULL, 1));
275}
276
277int
278_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
279		       const struct timespec * abstime)
280{
281
282	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
283	    abstime->tv_nsec >= 1000000000)
284		return (EINVAL);
285
286	return (cond_wait_common(cond, mutex, abstime, 0));
287}
288
289int
290__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
291		       const struct timespec *abstime)
292{
293
294	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
295	    abstime->tv_nsec >= 1000000000)
296		return (EINVAL);
297
298	return (cond_wait_common(cond, mutex, abstime, 1));
299}
300
301static int
302cond_signal_common(pthread_cond_t *cond, int broadcast)
303{
304	struct pthread	*curthread = _get_curthread();
305	pthread_cond_t	cv;
306	int		ret = 0, oldwaiters;
307
308	/*
309	 * If the condition variable is statically initialized, perform dynamic
310	 * initialization.
311	 */
312	if (__predict_false(*cond == NULL &&
313	    (ret = init_static(curthread, cond)) != 0))
314		return (ret);
315
316	cv = *cond;
317	/* Lock the condition variable structure. */
318	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
319	if (cv->c_waiters) {
320		if (!broadcast) {
321			cv->c_wakeups++;
322			cv->c_waiters--;
323			cv->c_seqno++;
324			_thr_umtx_wake(&cv->c_seqno, 1);
325		} else {
326			oldwaiters = cv->c_waiters;
327			cv->c_wakeups += cv->c_waiters;
328			cv->c_waiters = 0;
329			cv->c_seqno++;
330			_thr_umtx_wake(&cv->c_seqno, oldwaiters);
331		}
332	}
333	THR_LOCK_RELEASE(curthread, &cv->c_lock);
334	return (ret);
335}
336
337int
338_pthread_cond_signal(pthread_cond_t * cond)
339{
340
341	return (cond_signal_common(cond, 0));
342}
343
344int
345_pthread_cond_broadcast(pthread_cond_t * cond)
346{
347
348	return (cond_signal_common(cond, 1));
349}
350