thr_cond.c revision 211524
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 211524 2010-08-20 05:15:39Z davidxu $
27112918Sjeff */
28144518Sdavidxu
29157457Sdavidxu#include "namespace.h"
30112918Sjeff#include <stdlib.h>
31112918Sjeff#include <errno.h>
32112918Sjeff#include <string.h>
33112918Sjeff#include <pthread.h>
34144518Sdavidxu#include <limits.h>
35157457Sdavidxu#include "un-namespace.h"
36144518Sdavidxu
37112918Sjeff#include "thr_private.h"
38112918Sjeff
39112918Sjeff/*
40144518Sdavidxu * Prototypes
41115389Smtm */
42157457Sdavidxuint	__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
43157457Sdavidxuint	__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
44157457Sdavidxu		       const struct timespec * abstime);
45144518Sdavidxustatic int cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
46144518Sdavidxustatic int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
47144518Sdavidxu		    const struct timespec *abstime, int cancel);
48144518Sdavidxustatic int cond_signal_common(pthread_cond_t *cond, int broadcast);
49115389Smtm
50115389Smtm/*
51144518Sdavidxu * Double underscore versions are cancellation points.  Single underscore
52144518Sdavidxu * versions are not and are provided for libc internal usage (which
53144518Sdavidxu * shouldn't introduce cancellation points).
54112918Sjeff */
55144518Sdavidxu__weak_reference(__pthread_cond_wait, pthread_cond_wait);
56144518Sdavidxu__weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait);
57112918Sjeff
58112918Sjeff__weak_reference(_pthread_cond_init, pthread_cond_init);
59112918Sjeff__weak_reference(_pthread_cond_destroy, pthread_cond_destroy);
60112918Sjeff__weak_reference(_pthread_cond_signal, pthread_cond_signal);
61112918Sjeff__weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
62112918Sjeff
63144518Sdavidxustatic int
64144518Sdavidxucond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
65144518Sdavidxu{
66144518Sdavidxu	pthread_cond_t	pcond;
67144518Sdavidxu	int             rval = 0;
68112918Sjeff
69144518Sdavidxu	if ((pcond = (pthread_cond_t)
70164877Sdavidxu	    calloc(1, sizeof(struct pthread_cond))) == NULL) {
71144518Sdavidxu		rval = ENOMEM;
72144518Sdavidxu	} else {
73144518Sdavidxu		/*
74144518Sdavidxu		 * Initialise the condition variable structure:
75144518Sdavidxu		 */
76144518Sdavidxu		if (cond_attr == NULL || *cond_attr == NULL) {
77144518Sdavidxu			pcond->c_pshared = 0;
78144518Sdavidxu			pcond->c_clockid = CLOCK_REALTIME;
79144518Sdavidxu		} else {
80144518Sdavidxu			pcond->c_pshared = (*cond_attr)->c_pshared;
81144518Sdavidxu			pcond->c_clockid = (*cond_attr)->c_clockid;
82144518Sdavidxu		}
83164877Sdavidxu		_thr_umutex_init(&pcond->c_lock);
84144518Sdavidxu		*cond = pcond;
85144518Sdavidxu	}
86144518Sdavidxu	/* Return the completion status: */
87144518Sdavidxu	return (rval);
88144518Sdavidxu}
89112918Sjeff
90144518Sdavidxustatic int
91144518Sdavidxuinit_static(struct pthread *thread, pthread_cond_t *cond)
92112918Sjeff{
93144518Sdavidxu	int ret;
94112918Sjeff
95144518Sdavidxu	THR_LOCK_ACQUIRE(thread, &_cond_static_lock);
96144518Sdavidxu
97112918Sjeff	if (*cond == NULL)
98144518Sdavidxu		ret = cond_init(cond, NULL);
99144518Sdavidxu	else
100144518Sdavidxu		ret = 0;
101112918Sjeff
102144518Sdavidxu	THR_LOCK_RELEASE(thread, &_cond_static_lock);
103112918Sjeff
104144518Sdavidxu	return (ret);
105112918Sjeff}
106112918Sjeff
107112918Sjeffint
108112918Sjeff_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
109112918Sjeff{
110112918Sjeff
111144518Sdavidxu	*cond = NULL;
112144518Sdavidxu	return (cond_init(cond, cond_attr));
113112918Sjeff}
114112918Sjeff
115112918Sjeffint
116112918Sjeff_pthread_cond_destroy(pthread_cond_t *cond)
117112918Sjeff{
118164877Sdavidxu	struct pthread		*curthread = _get_curthread();
119144518Sdavidxu	struct pthread_cond	*cv;
120144518Sdavidxu	int			rval = 0;
121112918Sjeff
122144518Sdavidxu	if (*cond == NULL)
123144518Sdavidxu		rval = EINVAL;
124144518Sdavidxu	else {
125164877Sdavidxu		cv = *cond;
126164877Sdavidxu		THR_UMUTEX_LOCK(curthread, &cv->c_lock);
127144518Sdavidxu		/*
128144518Sdavidxu		 * NULL the caller's pointer now that the condition
129144518Sdavidxu		 * variable has been destroyed:
130144518Sdavidxu		 */
131144518Sdavidxu		*cond = NULL;
132164877Sdavidxu		THR_UMUTEX_UNLOCK(curthread, &cv->c_lock);
133112918Sjeff
134144518Sdavidxu		/*
135144518Sdavidxu		 * Free the memory allocated for the condition
136144518Sdavidxu		 * variable structure:
137144518Sdavidxu		 */
138144518Sdavidxu		free(cv);
139144518Sdavidxu	}
140144518Sdavidxu	/* Return the completion status: */
141112918Sjeff	return (rval);
142112918Sjeff}
143112918Sjeff
144144518Sdavidxustruct cond_cancel_info
145112918Sjeff{
146144518Sdavidxu	pthread_mutex_t	*mutex;
147144518Sdavidxu	pthread_cond_t	*cond;
148157591Sdavidxu	int		count;
149144518Sdavidxu};
150115035Smtm
151144518Sdavidxustatic void
152144518Sdavidxucond_cancel_handler(void *arg)
153144518Sdavidxu{
154144518Sdavidxu	struct pthread *curthread = _get_curthread();
155157591Sdavidxu	struct cond_cancel_info *info = (struct cond_cancel_info *)arg;
156164877Sdavidxu	pthread_cond_t  cv;
157144518Sdavidxu
158165110Sdavidxu	if (info->cond != NULL) {
159165110Sdavidxu		cv = *(info->cond);
160164877Sdavidxu		THR_UMUTEX_UNLOCK(curthread, &cv->c_lock);
161165110Sdavidxu	}
162157591Sdavidxu	_mutex_cv_lock(info->mutex, info->count);
163115035Smtm}
164115035Smtm
165211524Sdavidxu/*
166211524Sdavidxu * Cancellation behaivor:
167211524Sdavidxu *   Thread may be canceled at start, if thread is canceled, it means it
168211524Sdavidxu *   did not get a wakeup from pthread_cond_signal(), otherwise, it is
169211524Sdavidxu *   not canceled.
170211524Sdavidxu *   Thread cancellation never cause wakeup from pthread_cond_signal()
171211524Sdavidxu *   to be lost.
172211524Sdavidxu */
173115035Smtmstatic int
174144518Sdavidxucond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
175144518Sdavidxu	const struct timespec *abstime, int cancel)
176115035Smtm{
177144518Sdavidxu	struct pthread	*curthread = _get_curthread();
178144518Sdavidxu	struct timespec ts, ts2, *tsp;
179157591Sdavidxu	struct cond_cancel_info info;
180144518Sdavidxu	pthread_cond_t  cv;
181144518Sdavidxu	int		ret = 0;
182112918Sjeff
183112918Sjeff	/*
184144518Sdavidxu	 * If the condition variable is statically initialized,
185144518Sdavidxu	 * perform the dynamic initialization:
186112918Sjeff	 */
187144518Sdavidxu	if (__predict_false(*cond == NULL &&
188144518Sdavidxu	    (ret = init_static(curthread, cond)) != 0))
189144518Sdavidxu		return (ret);
190112918Sjeff
191211524Sdavidxu	_thr_testcancel(curthread);
192211524Sdavidxu
193144518Sdavidxu	cv = *cond;
194164877Sdavidxu	THR_UMUTEX_LOCK(curthread, &cv->c_lock);
195157591Sdavidxu	ret = _mutex_cv_unlock(mutex, &info.count);
196144518Sdavidxu	if (ret) {
197164877Sdavidxu		THR_UMUTEX_UNLOCK(curthread, &cv->c_lock);
198144518Sdavidxu		return (ret);
199112918Sjeff	}
200164877Sdavidxu
201157591Sdavidxu	info.mutex = mutex;
202157591Sdavidxu	info.cond  = cond;
203112918Sjeff
204164877Sdavidxu	if (abstime != NULL) {
205164877Sdavidxu		clock_gettime(cv->c_clockid, &ts);
206164877Sdavidxu		TIMESPEC_SUB(&ts2, abstime, &ts);
207164877Sdavidxu		tsp = &ts2;
208164877Sdavidxu	} else
209164877Sdavidxu		tsp = NULL;
210112918Sjeff
211164877Sdavidxu	if (cancel) {
212164877Sdavidxu		THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info);
213211524Sdavidxu		_thr_cancel_enter_defer(curthread, 0);
214164877Sdavidxu		ret = _thr_ucond_wait(&cv->c_kerncv, &cv->c_lock, tsp, 1);
215165110Sdavidxu		info.cond = NULL;
216211524Sdavidxu		_thr_cancel_leave_defer(curthread, (ret != 0));
217164877Sdavidxu		THR_CLEANUP_POP(curthread, 0);
218144518Sdavidxu	} else {
219164877Sdavidxu		ret = _thr_ucond_wait(&cv->c_kerncv, &cv->c_lock, tsp, 0);
220112918Sjeff	}
221164877Sdavidxu	if (ret == EINTR)
222164877Sdavidxu		ret = 0;
223157591Sdavidxu	_mutex_cv_lock(mutex, info.count);
224144518Sdavidxu	return (ret);
225112918Sjeff}
226112918Sjeff
227112918Sjeffint
228144518Sdavidxu_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
229112918Sjeff{
230144518Sdavidxu
231144518Sdavidxu	return (cond_wait_common(cond, mutex, NULL, 0));
232112918Sjeff}
233112918Sjeff
234112918Sjeffint
235144518Sdavidxu__pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
236112918Sjeff{
237144518Sdavidxu
238144518Sdavidxu	return (cond_wait_common(cond, mutex, NULL, 1));
239115277Smtm}
240115277Smtm
241144518Sdavidxuint
242144518Sdavidxu_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
243144518Sdavidxu		       const struct timespec * abstime)
244115277Smtm{
245112918Sjeff
246144518Sdavidxu	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
247144518Sdavidxu	    abstime->tv_nsec >= 1000000000)
248112918Sjeff		return (EINVAL);
249112918Sjeff
250144518Sdavidxu	return (cond_wait_common(cond, mutex, abstime, 0));
251112918Sjeff}
252112918Sjeff
253144518Sdavidxuint
254144518Sdavidxu__pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
255144518Sdavidxu		       const struct timespec *abstime)
256112918Sjeff{
257112918Sjeff
258144518Sdavidxu	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
259144518Sdavidxu	    abstime->tv_nsec >= 1000000000)
260144518Sdavidxu		return (EINVAL);
261112918Sjeff
262144518Sdavidxu	return (cond_wait_common(cond, mutex, abstime, 1));
263112918Sjeff}
264112918Sjeff
265144518Sdavidxustatic int
266144518Sdavidxucond_signal_common(pthread_cond_t *cond, int broadcast)
267112918Sjeff{
268144518Sdavidxu	struct pthread	*curthread = _get_curthread();
269144518Sdavidxu	pthread_cond_t	cv;
270164877Sdavidxu	int		ret = 0;
271112918Sjeff
272112918Sjeff	/*
273144518Sdavidxu	 * If the condition variable is statically initialized, perform dynamic
274144518Sdavidxu	 * initialization.
275112918Sjeff	 */
276144518Sdavidxu	if (__predict_false(*cond == NULL &&
277144518Sdavidxu	    (ret = init_static(curthread, cond)) != 0))
278144518Sdavidxu		return (ret);
279144518Sdavidxu
280144518Sdavidxu	cv = *cond;
281164877Sdavidxu	THR_UMUTEX_LOCK(curthread, &cv->c_lock);
282165110Sdavidxu	if (!broadcast)
283165110Sdavidxu		ret = _thr_ucond_signal(&cv->c_kerncv);
284165110Sdavidxu	else
285165110Sdavidxu		ret = _thr_ucond_broadcast(&cv->c_kerncv);
286164877Sdavidxu	THR_UMUTEX_UNLOCK(curthread, &cv->c_lock);
287144518Sdavidxu	return (ret);
288112918Sjeff}
289112918Sjeff
290144518Sdavidxuint
291144518Sdavidxu_pthread_cond_signal(pthread_cond_t * cond)
292112918Sjeff{
293112918Sjeff
294144518Sdavidxu	return (cond_signal_common(cond, 0));
295112918Sjeff}
296115389Smtm
297144518Sdavidxuint
298144518Sdavidxu_pthread_cond_broadcast(pthread_cond_t * cond)
299115389Smtm{
300144518Sdavidxu
301144518Sdavidxu	return (cond_signal_common(cond, 1));
302115389Smtm}
303