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