xref: /netbsd-src/lib/libpthread/pthread_cond.c (revision 53b02e147d4ed531c0d2a5ca9b3e8026ba3e99b5)
1 /*	$NetBSD: pthread_cond.c,v 1.76 2020/06/14 21:33:28 ad Exp $	*/
2 
3 /*-
4  * Copyright (c) 2001, 2006, 2007, 2008, 2020 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 and Andrew Doran.
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  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __RCSID("$NetBSD: pthread_cond.c,v 1.76 2020/06/14 21:33:28 ad Exp $");
34 
35 #include <stdlib.h>
36 #include <errno.h>
37 #include <sys/time.h>
38 #include <sys/types.h>
39 
40 #include "pthread.h"
41 #include "pthread_int.h"
42 #include "reentrant.h"
43 
44 int	_sys___nanosleep50(const struct timespec *, struct timespec *);
45 
46 int	_pthread_cond_has_waiters_np(pthread_cond_t *);
47 
48 __weak_alias(pthread_cond_has_waiters_np,_pthread_cond_has_waiters_np)
49 
50 __strong_alias(__libc_cond_init,pthread_cond_init)
51 __strong_alias(__libc_cond_signal,pthread_cond_signal)
52 __strong_alias(__libc_cond_broadcast,pthread_cond_broadcast)
53 __strong_alias(__libc_cond_wait,pthread_cond_wait)
54 __strong_alias(__libc_cond_timedwait,pthread_cond_timedwait)
55 __strong_alias(__libc_cond_destroy,pthread_cond_destroy)
56 
57 /*
58  * A dummy waiter that's used to flag that pthread_cond_signal() is in
59  * progress and nobody else should try to modify the waiter list until
60  * it completes.
61  */
62 static struct pthread__waiter pthread__cond_dummy;
63 
64 static clockid_t
65 pthread_cond_getclock(const pthread_cond_t *cond)
66 {
67 
68 	pthread__error(EINVAL, "Invalid condition variable",
69 	    cond->ptc_magic == _PT_COND_MAGIC);
70 
71 	return cond->ptc_private ?
72 	    *(clockid_t *)cond->ptc_private : CLOCK_REALTIME;
73 }
74 
75 int
76 pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr)
77 {
78 	if (__predict_false(__uselibcstub))
79 		return __libc_cond_init_stub(cond, attr);
80 
81 	pthread__error(EINVAL, "Invalid condition variable attribute",
82 	    (attr == NULL) || (attr->ptca_magic == _PT_CONDATTR_MAGIC));
83 
84 	cond->ptc_magic = _PT_COND_MAGIC;
85 	cond->ptc_waiters = NULL;
86 	cond->ptc_mutex = NULL;
87 	if (attr && attr->ptca_private) {
88 		cond->ptc_private = malloc(sizeof(clockid_t));
89 		if (cond->ptc_private == NULL)
90 			return errno;
91 		*(clockid_t *)cond->ptc_private =
92 		    *(clockid_t *)attr->ptca_private;
93 	} else
94 		cond->ptc_private = NULL;
95 
96 	return 0;
97 }
98 
99 
100 int
101 pthread_cond_destroy(pthread_cond_t *cond)
102 {
103 	if (__predict_false(__uselibcstub))
104 		return __libc_cond_destroy_stub(cond);
105 
106 	pthread__error(EINVAL, "Invalid condition variable",
107 	    cond->ptc_magic == _PT_COND_MAGIC);
108 	pthread__error(EBUSY, "Destroying condition variable in use",
109 	    cond->ptc_waiters == NULL);
110 
111 	cond->ptc_magic = _PT_COND_DEAD;
112 	free(cond->ptc_private);
113 
114 	return 0;
115 }
116 
117 int
118 pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
119 		       const struct timespec *abstime)
120 {
121 	struct pthread__waiter waiter, *next, *head;
122 	pthread_t self;
123 	int error, cancel;
124 	clockid_t clkid = pthread_cond_getclock(cond);
125 
126 	if (__predict_false(__uselibcstub))
127 		return __libc_cond_timedwait_stub(cond, mutex, abstime);
128 
129 	pthread__error(EINVAL, "Invalid condition variable",
130 	    cond->ptc_magic == _PT_COND_MAGIC);
131 	pthread__error(EINVAL, "Invalid mutex",
132 	    mutex->ptm_magic == _PT_MUTEX_MAGIC);
133 	pthread__error(EPERM, "Mutex not locked in condition wait",
134 	    mutex->ptm_owner != NULL);
135 
136 	self = pthread__self();
137 	pthread__assert(self->pt_lid != 0);
138 
139 	if (__predict_false(self->pt_cancel)) {
140 		pthread__cancelled();
141 	}
142 
143 	/* Note this thread as waiting on the CV. */
144 	cond->ptc_mutex = mutex;
145 	for (head = cond->ptc_waiters;; head = next) {
146 		/* Wait while pthread_cond_signal() in progress. */
147 		if (__predict_false(head == &pthread__cond_dummy)) {
148 			sched_yield();
149 			next = cond->ptc_waiters;
150 			continue;
151 		}
152 		waiter.lid = self->pt_lid;
153 		waiter.next = head;
154 #ifndef PTHREAD__ATOMIC_IS_MEMBAR
155 		membar_producer();
156 #endif
157 		next = atomic_cas_ptr(&cond->ptc_waiters, head, &waiter);
158 		if (__predict_true(next == head)) {
159 			break;
160 		}
161 	}
162 
163 	/* Drop the interlock and wait. */
164 	error = 0;
165 	pthread_mutex_unlock(mutex);
166 	while (waiter.lid && !(cancel = self->pt_cancel)) {
167 		int rv = _lwp_park(clkid, TIMER_ABSTIME, __UNCONST(abstime),
168 		    0, NULL, NULL);
169 		if (rv == 0) {
170 			continue;
171 		}
172 		if (errno != EINTR && errno != EALREADY) {
173 			error = errno;
174 			break;
175 		}
176 	}
177 	pthread_mutex_lock(mutex);
178 
179 	/*
180 	 * If this thread absorbed a wakeup from pthread_cond_signal() and
181 	 * cannot take the wakeup, we should ensure that another thread does.
182 	 *
183 	 * And if awoken early, we may still be on the waiter list and must
184 	 * remove self.
185 	 */
186 	if (__predict_false(cancel | error)) {
187 		pthread_cond_broadcast(cond);
188 
189 		/*
190 		 * Might have raced with another thread to do the wakeup.
191 		 * Wait until released, otherwise "waiter" is still globally
192 		 * visible.
193 		 */
194 		pthread_mutex_unlock(mutex);
195 		while (__predict_false(waiter.lid)) {
196 			(void)_lwp_park(CLOCK_MONOTONIC, 0, NULL, 0, NULL,
197 			    NULL);
198 		}
199 		pthread_mutex_lock(mutex);
200 	} else {
201 		pthread__assert(!waiter.lid);
202 	}
203 
204 	/*
205 	 * If cancelled then exit.  POSIX dictates that the mutex must be
206 	 * held if this happens.
207 	 */
208 	if (cancel) {
209 		pthread__cancelled();
210 	}
211 
212 	return error;
213 }
214 
215 int
216 pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
217 {
218 	if (__predict_false(__uselibcstub))
219 		return __libc_cond_wait_stub(cond, mutex);
220 
221 	return pthread_cond_timedwait(cond, mutex, NULL);
222 }
223 
224 int
225 pthread_cond_signal(pthread_cond_t *cond)
226 {
227 	struct pthread__waiter *head, *next;
228 	pthread_mutex_t *mutex;
229 	pthread_t self;
230 
231 	if (__predict_false(__uselibcstub))
232 		return __libc_cond_signal_stub(cond);
233 
234 	pthread__error(EINVAL, "Invalid condition variable",
235 	    cond->ptc_magic == _PT_COND_MAGIC);
236 
237 	/* Take ownership of one waiter. */
238 	self = pthread_self();
239 	mutex = cond->ptc_mutex;
240 	for (head = cond->ptc_waiters;; head = next) {
241 		/* Wait while pthread_cond_signal() in progress. */
242 		if (__predict_false(head == &pthread__cond_dummy)) {
243 			sched_yield();
244 			next = cond->ptc_waiters;
245 			continue;
246 		}
247 		if (head == NULL) {
248 			return 0;
249 		}
250 		/* Block concurrent access to the waiter list. */
251 		next = atomic_cas_ptr(&cond->ptc_waiters, head,
252 		    &pthread__cond_dummy);
253 		if (__predict_true(next == head)) {
254 			break;
255 		}
256 	}
257 
258 	/* Now that list is locked, read pointer to next and then unlock. */
259 	membar_enter();
260 	cond->ptc_waiters = head->next;
261 	membar_producer();
262 	head->next = NULL;
263 
264 	/* Now transfer waiter to the mutex. */
265 	pthread__mutex_deferwake(self, mutex, head);
266 	return 0;
267 }
268 
269 int
270 pthread_cond_broadcast(pthread_cond_t *cond)
271 {
272 	struct pthread__waiter *head, *next;
273 	pthread_mutex_t *mutex;
274 	pthread_t self;
275 
276 	if (__predict_false(__uselibcstub))
277 		return __libc_cond_broadcast_stub(cond);
278 
279 	pthread__error(EINVAL, "Invalid condition variable",
280 	    cond->ptc_magic == _PT_COND_MAGIC);
281 
282 	if (cond->ptc_waiters == NULL)
283 		return 0;
284 
285 	/* Take ownership of current set of waiters. */
286 	self = pthread_self();
287 	mutex = cond->ptc_mutex;
288 	for (head = cond->ptc_waiters;; head = next) {
289 		/* Wait while pthread_cond_signal() in progress. */
290 		if (__predict_false(head == &pthread__cond_dummy)) {
291 			sched_yield();
292 			next = cond->ptc_waiters;
293 			continue;
294 		}
295 		if (head == NULL) {
296 			return 0;
297 		}
298 		next = atomic_cas_ptr(&cond->ptc_waiters, head, NULL);
299 		if (__predict_true(next == head)) {
300 			break;
301 		}
302 	}
303 	membar_enter();
304 
305 	/* Now transfer waiters to the mutex. */
306 	pthread__mutex_deferwake(self, mutex, head);
307 	return 0;
308 }
309 
310 int
311 _pthread_cond_has_waiters_np(pthread_cond_t *cond)
312 {
313 
314 	return cond->ptc_waiters != NULL;
315 }
316 
317 int
318 pthread_condattr_init(pthread_condattr_t *attr)
319 {
320 
321 	attr->ptca_magic = _PT_CONDATTR_MAGIC;
322 	attr->ptca_private = NULL;
323 
324 	return 0;
325 }
326 
327 int
328 pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clck)
329 {
330 
331 	pthread__error(EINVAL, "Invalid condition variable attribute",
332 	    attr->ptca_magic == _PT_CONDATTR_MAGIC);
333 
334 	switch (clck) {
335 	case CLOCK_MONOTONIC:
336 	case CLOCK_REALTIME:
337 		if (attr->ptca_private == NULL)
338 			attr->ptca_private = malloc(sizeof(clockid_t));
339 		if (attr->ptca_private == NULL)
340 			return errno;
341 		*(clockid_t *)attr->ptca_private = clck;
342 		return 0;
343 	default:
344 		return EINVAL;
345 	}
346 }
347 
348 int
349 pthread_condattr_getclock(const pthread_condattr_t *__restrict attr,
350     clockid_t *__restrict clock_id)
351 {
352 
353 	pthread__error(EINVAL, "Invalid condition variable attribute",
354 	    attr->ptca_magic == _PT_CONDATTR_MAGIC);
355 
356 	if (attr == NULL || attr->ptca_private == NULL)
357 		return EINVAL;
358 	*clock_id = *(clockid_t *)attr->ptca_private;
359 	return 0;
360 }
361 
362 int
363 pthread_condattr_destroy(pthread_condattr_t *attr)
364 {
365 
366 	pthread__error(EINVAL, "Invalid condition variable attribute",
367 	    attr->ptca_magic == _PT_CONDATTR_MAGIC);
368 
369 	attr->ptca_magic = _PT_CONDATTR_DEAD;
370 	free(attr->ptca_private);
371 
372 	return 0;
373 }
374 
375 #ifdef _PTHREAD_PSHARED
376 int
377 pthread_condattr_getpshared(const pthread_condattr_t * __restrict attr,
378     int * __restrict pshared)
379 {
380 
381 	pthread__error(EINVAL, "Invalid condition variable attribute",
382 	    attr->ptca_magic == _PT_CONDATTR_MAGIC);
383 
384 	*pshared = PTHREAD_PROCESS_PRIVATE;
385 	return 0;
386 }
387 
388 int
389 pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared)
390 {
391 
392 	pthread__error(EINVAL, "Invalid condition variable attribute",
393 	    attr->ptca_magic == _PT_CONDATTR_MAGIC);
394 
395 	switch(pshared) {
396 	case PTHREAD_PROCESS_PRIVATE:
397 		return 0;
398 	case PTHREAD_PROCESS_SHARED:
399 		return ENOSYS;
400 	}
401 	return EINVAL;
402 }
403 #endif
404