thr_cond.c revision 144518
1112918Sjeff/*
2144518Sdavidxu * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
3112918Sjeff * All rights reserved.
4112918Sjeff *
5112918Sjeff * Redistribution and use in source and binary forms, with or without
6112918Sjeff * modification, are permitted provided that the following conditions
7112918Sjeff * are met:
8112918Sjeff * 1. Redistributions of source code must retain the above copyright
9144518Sdavidxu *    notice unmodified, this list of conditions, and the following
10144518Sdavidxu *    disclaimer.
11112918Sjeff * 2. Redistributions in binary form must reproduce the above copyright
12112918Sjeff *    notice, this list of conditions and the following disclaimer in the
13112918Sjeff *    documentation and/or other materials provided with the distribution.
14112918Sjeff *
15144518Sdavidxu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16144518Sdavidxu * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17144518Sdavidxu * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18144518Sdavidxu * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19144518Sdavidxu * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20144518Sdavidxu * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21144518Sdavidxu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22144518Sdavidxu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23144518Sdavidxu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24144518Sdavidxu * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25112918Sjeff *
26112918Sjeff * $FreeBSD: head/lib/libthr/thread/thr_cond.c 144518 2005-04-02 01:20:00Z davidxu $
27112918Sjeff */
28144518Sdavidxu
29112918Sjeff#include <stdlib.h>
30112918Sjeff#include <errno.h>
31112918Sjeff#include <string.h>
32112918Sjeff#include <pthread.h>
33144518Sdavidxu#include <limits.h>
34144518Sdavidxu
35112918Sjeff#include "thr_private.h"
36112918Sjeff
37112918Sjeff/*
38144518Sdavidxu * Prototypes
39115389Smtm */
40144518Sdavidxustatic int cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
41144518Sdavidxustatic int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
42144518Sdavidxu		    const struct timespec *abstime, int cancel);
43144518Sdavidxustatic int cond_signal_common(pthread_cond_t *cond, int broadcast);
44115389Smtm
45115389Smtm/*
46144518Sdavidxu * Double underscore versions are cancellation points.  Single underscore
47144518Sdavidxu * versions are not and are provided for libc internal usage (which
48144518Sdavidxu * shouldn't introduce cancellation points).
49112918Sjeff */
50144518Sdavidxu__weak_reference(__pthread_cond_wait, pthread_cond_wait);
51144518Sdavidxu__weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait);
52112918Sjeff
53112918Sjeff__weak_reference(_pthread_cond_init, pthread_cond_init);
54112918Sjeff__weak_reference(_pthread_cond_destroy, pthread_cond_destroy);
55112918Sjeff__weak_reference(_pthread_cond_signal, pthread_cond_signal);
56112918Sjeff__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
57112918Sjeff
58144518Sdavidxustatic int
59144518Sdavidxucond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
60144518Sdavidxu{
61144518Sdavidxu	pthread_cond_t	pcond;
62144518Sdavidxu	int             rval = 0;
63112918Sjeff
64144518Sdavidxu	if ((pcond = (pthread_cond_t)
65144518Sdavidxu	    malloc(sizeof(struct pthread_cond))) == NULL) {
66144518Sdavidxu		rval = ENOMEM;
67144518Sdavidxu	} else {
68144518Sdavidxu		/*
69144518Sdavidxu		 * Initialise the condition variable structure:
70144518Sdavidxu		 */
71144518Sdavidxu		_thr_umtx_init(&pcond->c_lock);
72144518Sdavidxu		pcond->c_seqno = 0;
73144518Sdavidxu		pcond->c_waiters = 0;
74144518Sdavidxu		pcond->c_wakeups = 0;
75144518Sdavidxu		if (cond_attr == NULL || *cond_attr == NULL) {
76144518Sdavidxu			pcond->c_pshared = 0;
77144518Sdavidxu			pcond->c_clockid = CLOCK_REALTIME;
78144518Sdavidxu		} else {
79144518Sdavidxu			pcond->c_pshared = (*cond_attr)->c_pshared;
80144518Sdavidxu			pcond->c_clockid = (*cond_attr)->c_clockid;
81144518Sdavidxu		}
82144518Sdavidxu		*cond = pcond;
83144518Sdavidxu	}
84144518Sdavidxu	/* Return the completion status: */
85144518Sdavidxu	return (rval);
86144518Sdavidxu}
87112918Sjeff
88144518Sdavidxustatic int
89144518Sdavidxuinit_static(struct pthread *thread, pthread_cond_t *cond)
90112918Sjeff{
91144518Sdavidxu	int ret;
92112918Sjeff
93144518Sdavidxu	THR_LOCK_ACQUIRE(thread, &_cond_static_lock);
94144518Sdavidxu
95112918Sjeff	if (*cond == NULL)
96144518Sdavidxu		ret = cond_init(cond, NULL);
97144518Sdavidxu	else
98144518Sdavidxu		ret = 0;
99112918Sjeff
100144518Sdavidxu	THR_LOCK_RELEASE(thread, &_cond_static_lock);
101112918Sjeff
102144518Sdavidxu	return (ret);
103112918Sjeff}
104112918Sjeff
105112918Sjeffint
106112918Sjeff_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
107112918Sjeff{
108112918Sjeff
109144518Sdavidxu	*cond = NULL;
110144518Sdavidxu	return (cond_init(cond, cond_attr));
111112918Sjeff}
112112918Sjeff
113112918Sjeffint
114112918Sjeff_pthread_cond_destroy(pthread_cond_t *cond)
115112918Sjeff{
116144518Sdavidxu	struct pthread_cond	*cv;
117144518Sdavidxu	struct pthread		*curthread = _get_curthread();
118144518Sdavidxu	int			rval = 0;
119112918Sjeff
120144518Sdavidxu	if (*cond == NULL)
121144518Sdavidxu		rval = EINVAL;
122144518Sdavidxu	else {
123144518Sdavidxu		/* Lock the condition variable structure: */
124144518Sdavidxu		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
125144518Sdavidxu		if ((*cond)->c_waiters + (*cond)->c_wakeups != 0) {
126144518Sdavidxu			THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
127144518Sdavidxu			return (EBUSY);
128144518Sdavidxu		}
129112918Sjeff
130144518Sdavidxu		/*
131144518Sdavidxu		 * NULL the caller's pointer now that the condition
132144518Sdavidxu		 * variable has been destroyed:
133144518Sdavidxu		 */
134144518Sdavidxu		cv = *cond;
135144518Sdavidxu		*cond = NULL;
136112918Sjeff
137144518Sdavidxu		/* Unlock the condition variable structure: */
138144518Sdavidxu		THR_LOCK_RELEASE(curthread, &cv->c_lock);
139112918Sjeff
140144518Sdavidxu		/* Free the cond lock structure: */
141112918Sjeff
142144518Sdavidxu		/*
143144518Sdavidxu		 * Free the memory allocated for the condition
144144518Sdavidxu		 * variable structure:
145144518Sdavidxu		 */
146144518Sdavidxu		free(cv);
147112918Sjeff
148144518Sdavidxu	}
149144518Sdavidxu	/* Return the completion status: */
150112918Sjeff	return (rval);
151112918Sjeff}
152112918Sjeff
153144518Sdavidxustruct cond_cancel_info
154112918Sjeff{
155144518Sdavidxu	pthread_mutex_t	*mutex;
156144518Sdavidxu	pthread_cond_t	*cond;
157144518Sdavidxu	long		seqno;
158144518Sdavidxu};
159115035Smtm
160144518Sdavidxustatic void
161144518Sdavidxucond_cancel_handler(void *arg)
162144518Sdavidxu{
163144518Sdavidxu	struct pthread *curthread = _get_curthread();
164144518Sdavidxu	struct cond_cancel_info *cci = (struct cond_cancel_info *)arg;
165144518Sdavidxu	pthread_cond_t cv;
166144518Sdavidxu
167144518Sdavidxu	cv = *(cci->cond);
168144518Sdavidxu	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
169144518Sdavidxu	if (cv->c_seqno != cci->seqno && cv->c_wakeups != 0) {
170144518Sdavidxu		if (cv->c_waiters > 0) {
171144518Sdavidxu			cv->c_seqno++;
172144518Sdavidxu			_thr_umtx_wake(&cv->c_seqno, 1);
173144518Sdavidxu		} else
174144518Sdavidxu			cv->c_wakeups--;
175144518Sdavidxu	} else {
176144518Sdavidxu		cv->c_waiters--;
177144518Sdavidxu	}
178144518Sdavidxu	THR_LOCK_RELEASE(curthread, &cv->c_lock);
179144518Sdavidxu
180144518Sdavidxu	_mutex_cv_lock(cci->mutex);
181115035Smtm}
182115035Smtm
183115035Smtmstatic int
184144518Sdavidxucond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
185144518Sdavidxu	const struct timespec *abstime, int cancel)
186115035Smtm{
187144518Sdavidxu	struct pthread	*curthread = _get_curthread();
188144518Sdavidxu	struct timespec ts, ts2, *tsp;
189144518Sdavidxu	struct cond_cancel_info cci;
190144518Sdavidxu	pthread_cond_t  cv;
191144518Sdavidxu	long		seq, oldseq;
192144518Sdavidxu	int		oldcancel;
193144518Sdavidxu	int		ret = 0;
194112918Sjeff
195112918Sjeff	/*
196144518Sdavidxu	 * If the condition variable is statically initialized,
197144518Sdavidxu	 * perform the dynamic initialization:
198112918Sjeff	 */
199144518Sdavidxu	if (__predict_false(*cond == NULL &&
200144518Sdavidxu	    (ret = init_static(curthread, cond)) != 0))
201144518Sdavidxu		return (ret);
202112918Sjeff
203144518Sdavidxu	cv = *cond;
204144518Sdavidxu	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
205144518Sdavidxu	ret = _mutex_cv_unlock(mutex);
206144518Sdavidxu	if (ret) {
207144518Sdavidxu		THR_LOCK_RELEASE(curthread, &cv->c_lock);
208144518Sdavidxu		return (ret);
209112918Sjeff	}
210144518Sdavidxu	oldseq = seq = cv->c_seqno;
211144518Sdavidxu	cci.mutex = mutex;
212144518Sdavidxu	cci.cond  = cond;
213144518Sdavidxu	cci.seqno = oldseq;
214112918Sjeff
215144518Sdavidxu	cv->c_waiters++;
216144518Sdavidxu	do {
217144518Sdavidxu		THR_LOCK_RELEASE(curthread, &cv->c_lock);
218112918Sjeff
219144518Sdavidxu		if (abstime != NULL) {
220144518Sdavidxu			clock_gettime(cv->c_clockid, &ts);
221144518Sdavidxu			TIMESPEC_SUB(&ts2, abstime, &ts);
222144518Sdavidxu			tsp = &ts2;
223144518Sdavidxu		} else
224144518Sdavidxu			tsp = NULL;
225112918Sjeff
226144518Sdavidxu		if (cancel) {
227144518Sdavidxu			THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &cci);
228144518Sdavidxu			oldcancel = _thr_cancel_enter(curthread);
229144518Sdavidxu			ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp);
230144518Sdavidxu			_thr_cancel_leave(curthread, oldcancel);
231144518Sdavidxu			THR_CLEANUP_POP(curthread, 0);
232144518Sdavidxu		} else {
233144518Sdavidxu			ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp);
234129484Smtm		}
235112918Sjeff
236144518Sdavidxu		THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
237144518Sdavidxu		seq = cv->c_seqno;
238144518Sdavidxu		if (abstime != NULL && ret == ETIMEDOUT)
239144518Sdavidxu			break;
240112918Sjeff
241144518Sdavidxu		/*
242144518Sdavidxu		 * loop if we have never been told to wake up
243144518Sdavidxu		 * or we lost a race.
244144518Sdavidxu		 */
245144518Sdavidxu	} while (seq == oldseq || cv->c_wakeups == 0);
246144518Sdavidxu
247144518Sdavidxu	if (seq != oldseq && cv->c_wakeups != 0) {
248144518Sdavidxu		cv->c_wakeups--;
249144518Sdavidxu		ret = 0;
250144518Sdavidxu	} else {
251144518Sdavidxu		cv->c_waiters--;
252112918Sjeff	}
253144518Sdavidxu	THR_LOCK_RELEASE(curthread, &cv->c_lock);
254144518Sdavidxu	_mutex_cv_lock(mutex);
255144518Sdavidxu	return (ret);
256112918Sjeff}
257112918Sjeff
258112918Sjeffint
259144518Sdavidxu_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
260112918Sjeff{
261144518Sdavidxu
262144518Sdavidxu	return (cond_wait_common(cond, mutex, NULL, 0));
263112918Sjeff}
264112918Sjeff
265112918Sjeffint
266144518Sdavidxu__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
267112918Sjeff{
268144518Sdavidxu
269144518Sdavidxu	return (cond_wait_common(cond, mutex, NULL, 1));
270115277Smtm}
271115277Smtm
272144518Sdavidxuint
273144518Sdavidxu_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
274144518Sdavidxu		       const struct timespec * abstime)
275115277Smtm{
276112918Sjeff
277144518Sdavidxu	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
278144518Sdavidxu	    abstime->tv_nsec >= 1000000000)
279112918Sjeff		return (EINVAL);
280112918Sjeff
281144518Sdavidxu	return (cond_wait_common(cond, mutex, abstime, 0));
282112918Sjeff}
283112918Sjeff
284144518Sdavidxuint
285144518Sdavidxu__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
286144518Sdavidxu		       const struct timespec *abstime)
287112918Sjeff{
288112918Sjeff
289144518Sdavidxu	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
290144518Sdavidxu	    abstime->tv_nsec >= 1000000000)
291144518Sdavidxu		return (EINVAL);
292112918Sjeff
293144518Sdavidxu	return (cond_wait_common(cond, mutex, abstime, 1));
294112918Sjeff}
295112918Sjeff
296144518Sdavidxustatic int
297144518Sdavidxucond_signal_common(pthread_cond_t *cond, int broadcast)
298112918Sjeff{
299144518Sdavidxu	struct pthread	*curthread = _get_curthread();
300144518Sdavidxu	pthread_cond_t	cv;
301144518Sdavidxu	int		ret = 0, oldwaiters;
302112918Sjeff
303112918Sjeff	/*
304144518Sdavidxu	 * If the condition variable is statically initialized, perform dynamic
305144518Sdavidxu	 * initialization.
306112918Sjeff	 */
307144518Sdavidxu	if (__predict_false(*cond == NULL &&
308144518Sdavidxu	    (ret = init_static(curthread, cond)) != 0))
309144518Sdavidxu		return (ret);
310144518Sdavidxu
311144518Sdavidxu	cv = *cond;
312144518Sdavidxu	/* Lock the condition variable structure. */
313144518Sdavidxu	THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
314144518Sdavidxu	if (cv->c_waiters) {
315144518Sdavidxu		if (!broadcast) {
316144518Sdavidxu			cv->c_wakeups++;
317144518Sdavidxu			cv->c_waiters--;
318144518Sdavidxu			cv->c_seqno++;
319144518Sdavidxu			_thr_umtx_wake(&cv->c_seqno, 1);
320144518Sdavidxu		} else {
321144518Sdavidxu			oldwaiters = cv->c_waiters;
322144518Sdavidxu			cv->c_wakeups += cv->c_waiters;
323144518Sdavidxu			cv->c_waiters = 0;
324144518Sdavidxu			cv->c_seqno++;
325144518Sdavidxu			_thr_umtx_wake(&cv->c_seqno, oldwaiters);
326144518Sdavidxu		}
327112918Sjeff	}
328144518Sdavidxu	THR_LOCK_RELEASE(curthread, &cv->c_lock);
329144518Sdavidxu	return (ret);
330112918Sjeff}
331112918Sjeff
332144518Sdavidxuint
333144518Sdavidxu_pthread_cond_signal(pthread_cond_t * cond)
334112918Sjeff{
335112918Sjeff
336144518Sdavidxu	return (cond_signal_common(cond, 0));
337112918Sjeff}
338115389Smtm
339144518Sdavidxuint
340144518Sdavidxu_pthread_cond_broadcast(pthread_cond_t * cond)
341115389Smtm{
342144518Sdavidxu
343144518Sdavidxu	return (cond_signal_common(cond, 1));
344115389Smtm}
345