thr_cond.c revision 162061
1228753Smm/*
2228753Smm * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
3248616Smm * All rights reserved.
4228753Smm *
5228753Smm * Redistribution and use in source and binary forms, with or without
6228753Smm * modification, are permitted provided that the following conditions
7228753Smm * are met:
8228753Smm * 1. Redistributions of source code must retain the above copyright
9228753Smm *    notice unmodified, this list of conditions, and the following
10228753Smm *    disclaimer.
11228753Smm * 2. Redistributions in binary form must reproduce the above copyright
12228753Smm *    notice, this list of conditions and the following disclaimer in the
13228753Smm *    documentation and/or other materials provided with the distribution.
14228753Smm *
15228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18228753Smm * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25228753Smm *
26228753Smm * $FreeBSD: head/lib/libthr/thread/thr_cond.c 162061 2006-09-06 04:04:10Z davidxu $
27228753Smm */
28231200Smm
29228753Smm#include "namespace.h"
30228753Smm#include <stdlib.h>
31228753Smm#include <errno.h>
32228753Smm#include <string.h>
33228753Smm#include <pthread.h>
34228753Smm#include <limits.h>
35228753Smm#include "un-namespace.h"
36228753Smm
37228753Smm#include "thr_private.h"
38228753Smm
39228753Smm/*
40228753Smm * Prototypes
41228753Smm */
42228753Smmint	__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
43228753Smmint	__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
44228753Smm		       const struct timespec * abstime);
45228753Smmstatic int cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
46228753Smmstatic int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
47228753Smm		    const struct timespec *abstime, int cancel);
48228753Smmstatic int cond_signal_common(pthread_cond_t *cond, int broadcast);
49228753Smm
50228753Smm/*
51228753Smm * Double underscore versions are cancellation points.  Single underscore
52228753Smm * versions are not and are provided for libc internal usage (which
53228753Smm * shouldn't introduce cancellation points).
54228753Smm */
55228753Smm__weak_reference(__pthread_cond_wait, pthread_cond_wait);
56228753Smm__weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait);
57248616Smm
58228753Smm__weak_reference(_pthread_cond_init, pthread_cond_init);
59248616Smm__weak_reference(_pthread_cond_destroy, pthread_cond_destroy);
60228753Smm__weak_reference(_pthread_cond_signal, pthread_cond_signal);
61231200Smm__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
62231200Smm
63231200Smmstatic int
64228753Smmcond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
65228753Smm{
66228753Smm	pthread_cond_t	pcond;
67231200Smm	int             rval = 0;
68228753Smm
69228753Smm	if ((pcond = (pthread_cond_t)
70231200Smm	    malloc(sizeof(struct pthread_cond))) == NULL) {
71231200Smm		rval = ENOMEM;
72231200Smm	} else {
73231200Smm		/*
74231200Smm		 * Initialise the condition variable structure:
75231200Smm		 */
76231200Smm		_thr_umutex_init(&pcond->c_lock);
77231200Smm		pcond->c_seqno = 0;
78228753Smm		pcond->c_waiters = 0;
79231200Smm		pcond->c_wakeups = 0;
80231200Smm		if (cond_attr == NULL || *cond_attr == NULL) {
81231200Smm			pcond->c_pshared = 0;
82231200Smm			pcond->c_clockid = CLOCK_REALTIME;
83231200Smm		} else {
84231200Smm			pcond->c_pshared = (*cond_attr)->c_pshared;
85228753Smm			pcond->c_clockid = (*cond_attr)->c_clockid;
86228753Smm		}
87228753Smm		*cond = pcond;
88228753Smm	}
89228753Smm	/* Return the completion status: */
90228753Smm	return (rval);
91248616Smm}
92228753Smm
93228753Smmstatic int
94228753Smminit_static(struct pthread *thread, pthread_cond_t *cond)
95228753Smm{
96228753Smm	int ret;
97228753Smm
98228753Smm	THR_LOCK_ACQUIRE(thread, &_cond_static_lock);
99228753Smm
100228753Smm	if (*cond == NULL)
101228753Smm		ret = cond_init(cond, NULL);
102228753Smm	else
103228753Smm		ret = 0;
104228753Smm
105228753Smm	THR_LOCK_RELEASE(thread, &_cond_static_lock);
106228753Smm
107248616Smm	return (ret);
108248616Smm}
109248616Smm
110248616Smmint
111228753Smm_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
112248616Smm{
113228753Smm
114228753Smm	*cond = NULL;
115228753Smm	return (cond_init(cond, cond_attr));
116228753Smm}
117228753Smm
118228753Smmint
119228753Smm_pthread_cond_destroy(pthread_cond_t *cond)
120228753Smm{
121228753Smm	struct pthread_cond	*cv;
122228753Smm	struct pthread		*curthread = _get_curthread();
123228753Smm	int			rval = 0;
124248616Smm
125228753Smm	if (*cond == NULL)
126248616Smm		rval = EINVAL;
127248616Smm	else {
128248616Smm		/* Lock the condition variable structure: */
129248616Smm		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
130248616Smm		if ((*cond)->c_waiters + (*cond)->c_wakeups != 0) {
131248616Smm			THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
132248616Smm			return (EBUSY);
133248616Smm		}
134248616Smm
135248616Smm		/*
136248616Smm		 * NULL the caller's pointer now that the condition
137248616Smm		 * variable has been destroyed:
138248616Smm		 */
139248616Smm		cv = *cond;
140248616Smm		*cond = NULL;
141248616Smm
142248616Smm		/* Unlock the condition variable structure: */
143248616Smm		THR_LOCK_RELEASE(curthread, &cv->c_lock);
144248616Smm
145248616Smm		/* Free the cond lock structure: */
146248616Smm
147248616Smm		/*
148228753Smm		 * Free the memory allocated for the condition
149231200Smm		 * variable structure:
150228753Smm		 */
151228753Smm		free(cv);
152228753Smm
153228753Smm	}
154228753Smm	/* Return the completion status: */
155228753Smm	return (rval);
156228753Smm}
157228753Smm
158228753Smmstruct cond_cancel_info
159231200Smm{
160228753Smm	pthread_mutex_t	*mutex;
161228753Smm	pthread_cond_t	*cond;
162228753Smm	long		seqno;
163228753Smm	int		count;
164228753Smm};
165248616Smm
166228753Smmstatic void
167248616Smmcond_cancel_handler(void *arg)
168228753Smm{
169248616Smm	struct pthread *curthread = _get_curthread();
170248616Smm	struct cond_cancel_info *info = (struct cond_cancel_info *)arg;
171228753Smm	pthread_cond_t cv;
172248616Smm
173248616Smm	cv = *(info->cond);
174248616Smm	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
175248616Smm	if (cv->c_seqno != info->seqno && cv->c_wakeups != 0) {
176248616Smm		if (cv->c_waiters > 0) {
177228753Smm			cv->c_seqno++;
178228753Smm			_thr_umtx_wake(&cv->c_seqno, 1);
179228753Smm		} else
180228753Smm			cv->c_wakeups--;
181228753Smm	} else {
182228753Smm		cv->c_waiters--;
183248616Smm	}
184248616Smm	THR_LOCK_RELEASE(curthread, &cv->c_lock);
185228753Smm
186228753Smm	_mutex_cv_lock(info->mutex, info->count);
187228753Smm}
188248616Smm
189248616Smmstatic int
190248616Smmcond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
191248616Smm	const struct timespec *abstime, int cancel)
192248616Smm{
193248616Smm	struct pthread	*curthread = _get_curthread();
194248616Smm	struct timespec ts, ts2, *tsp;
195248616Smm	struct cond_cancel_info info;
196248616Smm	pthread_cond_t  cv;
197248616Smm	long		seq, oldseq;
198248616Smm	int		oldcancel;
199228753Smm	int		ret = 0;
200228753Smm
201228753Smm	/*
202228753Smm	 * If the condition variable is statically initialized,
203228753Smm	 * perform the dynamic initialization:
204228753Smm	 */
205228753Smm	if (__predict_false(*cond == NULL &&
206228753Smm	    (ret = init_static(curthread, cond)) != 0))
207228753Smm		return (ret);
208228753Smm
209228753Smm	cv = *cond;
210228753Smm	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
211228753Smm	ret = _mutex_cv_unlock(mutex, &info.count);
212228753Smm	if (ret) {
213228753Smm		THR_LOCK_RELEASE(curthread, &cv->c_lock);
214228753Smm		return (ret);
215228753Smm	}
216228753Smm	oldseq = seq = cv->c_seqno;
217228753Smm	info.mutex = mutex;
218228753Smm	info.cond  = cond;
219228753Smm	info.seqno = oldseq;
220228753Smm
221228753Smm	cv->c_waiters++;
222228753Smm	do {
223228753Smm		THR_LOCK_RELEASE(curthread, &cv->c_lock);
224228753Smm
225228753Smm		if (abstime != NULL) {
226228753Smm			clock_gettime(cv->c_clockid, &ts);
227228753Smm			TIMESPEC_SUB(&ts2, abstime, &ts);
228228753Smm			tsp = &ts2;
229228753Smm		} else
230228753Smm			tsp = NULL;
231228753Smm
232228753Smm		if (cancel) {
233228753Smm			THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info);
234228753Smm			oldcancel = _thr_cancel_enter(curthread);
235228753Smm			ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp);
236228753Smm			_thr_cancel_leave(curthread, oldcancel);
237228753Smm			THR_CLEANUP_POP(curthread, 0);
238228753Smm		} else {
239228753Smm			ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp);
240228753Smm		}
241228753Smm
242228753Smm		THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
243228753Smm		seq = cv->c_seqno;
244228753Smm		if (abstime != NULL && ret == ETIMEDOUT)
245228753Smm			break;
246228753Smm
247228753Smm		/*
248228753Smm		 * loop if we have never been told to wake up
249228753Smm		 * or we lost a race.
250228753Smm		 */
251228753Smm	} while (seq == oldseq || cv->c_wakeups == 0);
252228753Smm
253228753Smm	if (seq != oldseq && cv->c_wakeups != 0) {
254228753Smm		cv->c_wakeups--;
255228753Smm		ret = 0;
256228753Smm	} else {
257248616Smm		cv->c_waiters--;
258248616Smm	}
259248616Smm	THR_LOCK_RELEASE(curthread, &cv->c_lock);
260228753Smm	_mutex_cv_lock(mutex, info.count);
261228753Smm	return (ret);
262228753Smm}
263228753Smm
264228753Smmint
265228753Smm_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
266228753Smm{
267228753Smm
268228753Smm	return (cond_wait_common(cond, mutex, NULL, 0));
269228753Smm}
270228753Smm
271228753Smmint
272228753Smm__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
273228753Smm{
274228753Smm
275228753Smm	return (cond_wait_common(cond, mutex, NULL, 1));
276228753Smm}
277228753Smm
278228753Smmint
279228753Smm_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
280228753Smm		       const struct timespec * abstime)
281228753Smm{
282228753Smm
283228753Smm	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
284228753Smm	    abstime->tv_nsec >= 1000000000)
285228753Smm		return (EINVAL);
286228753Smm
287228753Smm	return (cond_wait_common(cond, mutex, abstime, 0));
288228753Smm}
289228753Smm
290228753Smmint
291228753Smm__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
292228753Smm		       const struct timespec *abstime)
293228753Smm{
294228753Smm
295228753Smm	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
296228753Smm	    abstime->tv_nsec >= 1000000000)
297228753Smm		return (EINVAL);
298228753Smm
299228753Smm	return (cond_wait_common(cond, mutex, abstime, 1));
300228753Smm}
301228753Smm
302228753Smmstatic int
303228753Smmcond_signal_common(pthread_cond_t *cond, int broadcast)
304228753Smm{
305228753Smm	struct pthread	*curthread = _get_curthread();
306228753Smm	pthread_cond_t	cv;
307228753Smm	int		ret = 0, oldwaiters;
308228753Smm
309228753Smm	/*
310228753Smm	 * If the condition variable is statically initialized, perform dynamic
311228753Smm	 * initialization.
312248616Smm	 */
313248616Smm	if (__predict_false(*cond == NULL &&
314248616Smm	    (ret = init_static(curthread, cond)) != 0))
315228753Smm		return (ret);
316228753Smm
317228753Smm	cv = *cond;
318228753Smm	/* Lock the condition variable structure. */
319228753Smm	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
320248616Smm	if (cv->c_waiters) {
321248616Smm		if (!broadcast) {
322248616Smm			cv->c_wakeups++;
323248616Smm			cv->c_waiters--;
324248616Smm			cv->c_seqno++;
325248616Smm			_thr_umtx_wake(&cv->c_seqno, 1);
326248616Smm		} else {
327248616Smm			oldwaiters = cv->c_waiters;
328248616Smm			cv->c_wakeups += cv->c_waiters;
329248616Smm			cv->c_waiters = 0;
330248616Smm			cv->c_seqno++;
331248616Smm			_thr_umtx_wake(&cv->c_seqno, oldwaiters);
332248616Smm		}
333248616Smm	}
334248616Smm	THR_LOCK_RELEASE(curthread, &cv->c_lock);
335248616Smm	return (ret);
336248616Smm}
337248616Smm
338248616Smmint
339248616Smm_pthread_cond_signal(pthread_cond_t * cond)
340248616Smm{
341228753Smm
342228753Smm	return (cond_signal_common(cond, 0));
343228753Smm}
344228753Smm
345228753Smmint
346228753Smm_pthread_cond_broadcast(pthread_cond_t * cond)
347228753Smm{
348228753Smm
349228753Smm	return (cond_signal_common(cond, 1));
350228753Smm}
351228753Smm