xref: /netbsd-src/lib/libpthread/pthread_cond.c (revision 453f6b99a313f2f372963fe81f55bf6f811e3f55)
1 /*	$NetBSD: pthread_cond.c,v 1.5 2003/01/31 04:59:40 nathanw Exp $	*/
2 
3 /*-
4  * Copyright (c) 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Nathan J. Williams.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <assert.h>
40 #include <errno.h>
41 #include <sys/cdefs.h>
42 
43 #include "pthread.h"
44 #include "pthread_int.h"
45 
46 #undef PTHREAD_COND_DEBUG
47 
48 #ifdef PTHREAD_COND_DEBUG
49 #define SDPRINTF(x) DPRINTF(x)
50 #else
51 #define SDPRINTF(x)
52 #endif
53 
54 static void pthread_cond_wait__callback(void *);
55 
56 __strong_alias(__libc_cond_init,pthread_cond_init)
57 __strong_alias(__libc_cond_signal,pthread_cond_signal)
58 __strong_alias(__libc_cond_broadcast,pthread_cond_broadcast)
59 __strong_alias(__libc_cond_wait,pthread_cond_wait)
60 __strong_alias(__libc_cond_timedwait,pthread_cond_timedwait)
61 __strong_alias(__libc_cond_destroy,pthread_cond_destroy)
62 
63 int
64 pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr)
65 {
66 
67 #ifdef ERRORCHECK
68 	if ((cond == NULL) ||
69 	    (attr && (attr->ptca_magic != _PT_CONDATTR_MAGIC)))
70 		return EINVAL;
71 #endif
72 
73 	cond->ptc_magic = _PT_COND_MAGIC;
74 	pthread_lockinit(&cond->ptc_lock);
75 	PTQ_INIT(&cond->ptc_waiters);
76 	cond->ptc_mutex = NULL;
77 
78 	return 0;
79 }
80 
81 
82 int
83 pthread_cond_destroy(pthread_cond_t *cond)
84 {
85 
86 #ifdef ERRORCHECK
87 	if ((cond == NULL) || (cond->ptc_magic != _PT_COND_MAGIC) ||
88 	    (cond->ptc_mutex != NULL) ||
89 	    (cond->ptc_lock != __SIMPLELOCK_UNLOCKED))
90 		return EINVAL;
91 #endif
92 
93 	cond->ptc_magic = _PT_COND_DEAD;
94 
95 	return 0;
96 }
97 
98 
99 int
100 pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
101 {
102 	pthread_t self;
103 #ifdef ERRORCHECK
104 	if ((cond == NULL) || (cond->ptc_magic != _PT_COND_MAGIC) ||
105 	    (mutex == NULL) || (mutex->ptm_magic != _PT_MUTEX_MAGIC))
106 		return EINVAL;
107 #endif
108 	self = pthread__self();
109 	PTHREADD_ADD(PTHREADD_COND_WAIT);
110 	pthread_spinlock(self, &cond->ptc_lock);
111 #ifdef ERRORCHECK
112 	if (cond->ptc_mutex == NULL)
113 		cond->ptc_mutex = mutex;
114 	else {
115 		if (cond->ptc_mutex != mutex) {
116 			pthread_spinunlock(self, &cond->ptc_lock);
117 			return EINVAL;
118 		}
119 		/* Check the mutex is actually locked */
120 	        if (mutex->ptm_lock != __SIMPLELOCK_LOCKED) {
121 			pthread_spinunlock(self, &cond->ptc_lock);
122 			return EPERM;
123 		}
124 	}
125 #endif
126 	SDPRINTF(("(cond wait %p) Waiting on %p, mutex %p\n",
127 	    self, cond, mutex));
128 	pthread_spinlock(self, &self->pt_statelock);
129 	if (self->pt_cancel) {
130 		pthread_spinunlock(self, &self->pt_statelock);
131 		pthread_spinunlock(self, &cond->ptc_lock);
132 		pthread_exit(PTHREAD_CANCELED);
133 	}
134 	self->pt_state = PT_STATE_BLOCKED_QUEUE;
135 	self->pt_sleepobj = cond;
136 	self->pt_sleepq = &cond->ptc_waiters;
137 	self->pt_sleeplock = &cond->ptc_lock;
138 	pthread_spinunlock(self, &self->pt_statelock);
139 
140 	PTQ_INSERT_TAIL(&cond->ptc_waiters, self, pt_sleep);
141 	pthread_mutex_unlock(mutex);
142 
143 	pthread__block(self, &cond->ptc_lock);
144 	/* Spinlock is unlocked on return */
145 	pthread_mutex_lock(mutex);
146 	pthread__testcancel(self);
147 	SDPRINTF(("(cond wait %p) Woke up on %p, mutex %p\n",
148 	    self, cond, mutex));
149 
150 	return 0;
151 }
152 
153 
154 struct pthread_cond__waitarg {
155 	pthread_t ptw_thread;
156 	pthread_cond_t *ptw_cond;
157 };
158 
159 int
160 pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
161     const struct timespec *abstime)
162 {
163 	pthread_t self;
164 	struct pthread_cond__waitarg wait;
165 	struct pt_alarm_t alarm;
166 	int retval;
167 
168 #ifdef ERRORCHECK
169 	if ((cond == NULL) || (cond->ptc_magic != _PT_COND_MAGIC) ||
170 	    (mutex == NULL) || (mutex->ptm_magic != _PT_MUTEX_MAGIC))
171 		return EINVAL;
172 	if ((abstime == NULL) || (abstime->tv_sec < 0 ||
173 	    abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000))
174 		return EINVAL;
175 #endif
176 	self = pthread__self();
177 	pthread_spinlock(self, &cond->ptc_lock);
178 #ifdef ERRORCHECK
179 	if (cond->ptc_mutex == NULL)
180 		cond->ptc_mutex = mutex;
181 	else {
182 		if (cond->ptc_mutex != mutex) {
183 			pthread_spinunlock(self, &cond->ptc_lock);
184 			return EINVAL;
185 		}
186 		/* Check the mutex is actually locked */
187 	        if (mutex->ptm_lock != __SIMPLELOCK_LOCKED) {
188 			pthread_spinunlock(self, &cond->ptc_lock);
189 			return EPERM;
190 		}
191 	}
192 #endif
193 	PTHREADD_ADD(PTHREADD_COND_TIMEDWAIT);
194 	wait.ptw_thread = self;
195 	wait.ptw_cond = cond;
196 	retval = 0;
197 	SDPRINTF(("(cond timed wait %p) Waiting on %p until %d.%06ld\n",
198 	    self, cond, abstime->tv_sec, abstime->tv_nsec/1000));
199 
200 	pthread_spinlock(self, &self->pt_statelock);
201 	if (self->pt_cancel) {
202 		pthread_spinunlock(self, &self->pt_statelock);
203 		pthread_spinunlock(self, &cond->ptc_lock);
204 		pthread_exit(PTHREAD_CANCELED);
205 	}
206 	pthread__alarm_add(self, &alarm, abstime, pthread_cond_wait__callback,
207 	    &wait);
208 	self->pt_state = PT_STATE_BLOCKED_QUEUE;
209 	self->pt_sleepobj = cond;
210 	self->pt_sleepq = &cond->ptc_waiters;
211 	self->pt_sleeplock = &cond->ptc_lock;
212 	pthread_spinunlock(self, &self->pt_statelock);
213 
214 	PTQ_INSERT_TAIL(&cond->ptc_waiters, self, pt_sleep);
215 	pthread_mutex_unlock(mutex);
216 
217 	pthread__block(self, &cond->ptc_lock);
218 	/* Spinlock is unlocked on return */
219 	SDPRINTF(("(cond timed wait %p) Woke up on %p, mutex %p\n",
220 	    self, cond));
221 	pthread__alarm_del(self, &alarm);
222 	if (pthread__alarm_fired(&alarm))
223 		retval = ETIMEDOUT;
224 	SDPRINTF(("(cond timed wait %p) %s\n",
225 	    self, (retval == ETIMEDOUT) ? "(timed out)" : ""));
226 	pthread_mutex_lock(mutex);
227 	pthread__testcancel(self);
228 
229 	return retval;
230 }
231 
232 static void
233 pthread_cond_wait__callback(void *arg)
234 {
235 	struct pthread_cond__waitarg *a;
236 	pthread_t self;
237 
238 	a = arg;
239 	self = pthread__self();
240 
241 	/*
242 	 * Don't dequeue and schedule the thread if it's already been
243 	 * queued up by a signal or broadcast (but hasn't yet run as far
244 	 * as pthread__alarm_del(), or we wouldn't be here, and hence can't
245 	 * have become blocked on some *other* queue).
246 	 */
247 	pthread_spinlock(self, &a->ptw_cond->ptc_lock);
248 	if (a->ptw_thread->pt_state == PT_STATE_BLOCKED_QUEUE) {
249 		PTQ_REMOVE(&a->ptw_cond->ptc_waiters, a->ptw_thread, pt_sleep);
250 #ifdef ERRORCHECK
251 		if (PTQ_EMPTY(&a->ptw_cond->ptc_waiters))
252 			a->ptw_cond->ptc_mutex = NULL;
253 #endif
254 		pthread__sched(self, a->ptw_thread);
255 	}
256 	pthread_spinunlock(self, &a->ptw_cond->ptc_lock);
257 }
258 
259 int
260 pthread_cond_signal(pthread_cond_t *cond)
261 {
262 	pthread_t self, signaled;
263 #ifdef ERRORCHECK
264 	if ((cond == NULL) || (cond->ptc_magic != _PT_COND_MAGIC))
265 		return EINVAL;
266 #endif
267 	PTHREADD_ADD(PTHREADD_COND_SIGNAL);
268 
269 	SDPRINTF(("(cond signal %p) Signaling %p\n",
270 	    pthread__self(), cond));
271 
272 	if (!PTQ_EMPTY(&cond->ptc_waiters)) {
273 		self = pthread__self();
274 		pthread_spinlock(self, &cond->ptc_lock);
275 		signaled = PTQ_FIRST(&cond->ptc_waiters);
276 		if (signaled != NULL)
277 			PTQ_REMOVE(&cond->ptc_waiters, signaled, pt_sleep);
278 #ifdef ERRORCHECK
279 		if (PTQ_EMPTY(&cond->ptc_waiters))
280 			cond->ptc_mutex = NULL;
281 #endif
282 		pthread_spinunlock(self, &cond->ptc_lock);
283 		if (signaled != NULL)
284 			pthread__sched(self, signaled);
285 	}
286 
287 	return 0;
288 }
289 
290 
291 int
292 pthread_cond_broadcast(pthread_cond_t *cond)
293 {
294 	pthread_t self;
295 	struct pthread_queue_t blockedq;
296 #ifdef ERRORCHECK
297 	if ((cond == NULL) || (cond->ptc_magic != _PT_COND_MAGIC))
298 		return EINVAL;
299 #endif
300 
301 	PTHREADD_ADD(PTHREADD_COND_BROADCAST);
302 	SDPRINTF(("(cond signal %p) Broadcasting %p\n",
303 	    pthread__self(), cond));
304 
305 	if (!PTQ_EMPTY(&cond->ptc_waiters)) {
306 		self = pthread__self();
307 		pthread_spinlock(self, &cond->ptc_lock);
308 		blockedq = cond->ptc_waiters;
309 		PTQ_INIT(&cond->ptc_waiters);
310 #ifdef ERRORCHECK
311 		cond->ptc_mutex = NULL;
312 #endif
313 		pthread_spinunlock(self, &cond->ptc_lock);
314 		pthread__sched_sleepers(self, &blockedq);
315 	}
316 
317 	return 0;
318 
319 }
320 
321 
322 int
323 pthread_condattr_init(pthread_condattr_t *attr)
324 {
325 
326 #ifdef ERRORCHECK
327 	if (attr == NULL)
328 		return EINVAL;
329 #endif
330 
331 	attr->ptca_magic = _PT_CONDATTR_MAGIC;
332 
333 	return 0;
334 }
335 
336 
337 int
338 pthread_condattr_destroy(pthread_condattr_t *attr)
339 {
340 
341 #ifdef ERRORCHECK
342 	if ((attr == NULL) ||
343 	    (attr->ptca_magic != _PT_CONDATTR_MAGIC))
344 		return EINVAL;
345 #endif
346 
347 	attr->ptca_magic = _PT_CONDATTR_DEAD;
348 
349 	return 0;
350 }
351