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