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