pthread_cond.c revision 1.72 1 1.72 riastrad /* $NetBSD: pthread_cond.c,v 1.72 2020/06/04 04:40:01 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.72 riastrad __RCSID("$NetBSD: pthread_cond.c,v 1.72 2020/06/04 04:40:01 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.70 ad pthread_t self, next, waiters;
115 1.71 ad int retval, cancel;
116 1.63 christos clockid_t clkid = pthread_cond_getclock(cond);
117 1.10 nathanw
118 1.59 christos if (__predict_false(__uselibcstub))
119 1.59 christos return __libc_cond_timedwait_stub(cond, mutex, abstime);
120 1.59 christos
121 1.11 nathanw pthread__error(EINVAL, "Invalid condition variable",
122 1.11 nathanw cond->ptc_magic == _PT_COND_MAGIC);
123 1.11 nathanw pthread__error(EINVAL, "Invalid mutex",
124 1.11 nathanw mutex->ptm_magic == _PT_MUTEX_MAGIC);
125 1.11 nathanw pthread__error(EPERM, "Mutex not locked in condition wait",
126 1.35 ad mutex->ptm_owner != NULL);
127 1.10 nathanw
128 1.2 thorpej self = pthread__self();
129 1.71 ad pthread__assert(!self->pt_condwait);
130 1.6 nathanw
131 1.43 ad if (__predict_false(self->pt_cancel)) {
132 1.40 ad pthread__cancelled();
133 1.43 ad }
134 1.32 ad
135 1.43 ad /* Note this thread as waiting on the CV. */
136 1.36 ad cond->ptc_mutex = mutex;
137 1.70 ad self->pt_condwait = 1;
138 1.70 ad for (waiters = cond->ptc_waiters;; waiters = next) {
139 1.70 ad self->pt_condnext = waiters;
140 1.70 ad #ifndef PTHREAD__ATOMIC_IS_MEMBAR
141 1.70 ad membar_producer();
142 1.70 ad #endif
143 1.70 ad next = atomic_cas_ptr(&cond->ptc_waiters, waiters, self);
144 1.70 ad if (next == waiters)
145 1.70 ad break;
146 1.70 ad }
147 1.70 ad
148 1.70 ad /* Drop the interlock */
149 1.70 ad self->pt_willpark = 1;
150 1.70 ad pthread_mutex_unlock(mutex);
151 1.70 ad self->pt_willpark = 0;
152 1.32 ad
153 1.43 ad do {
154 1.70 ad pthread__assert(self->pt_nwaiters <= 1);
155 1.70 ad pthread__assert(self->pt_nwaiters != 0 ||
156 1.70 ad self->pt_waiters[0] == 0);
157 1.70 ad retval = _lwp_park(clkid, TIMER_ABSTIME, __UNCONST(abstime),
158 1.70 ad self->pt_waiters[0], NULL, NULL);
159 1.70 ad self->pt_waiters[0] = 0;
160 1.70 ad self->pt_nwaiters = 0;
161 1.46 ad if (__predict_false(retval != 0)) {
162 1.70 ad if (errno == EINTR || errno == EALREADY ||
163 1.70 ad errno == ESRCH) {
164 1.46 ad retval = 0;
165 1.70 ad } else {
166 1.46 ad retval = errno;
167 1.46 ad }
168 1.45 ad }
169 1.71 ad cancel = self->pt_cancel;
170 1.71 ad } while (self->pt_condwait && !cancel && !retval);
171 1.70 ad
172 1.70 ad /*
173 1.71 ad * If this thread absorbed a wakeup from pthread_cond_signal() and
174 1.71 ad * cannot take the wakeup, we must ensure that another thread does.
175 1.70 ad *
176 1.71 ad * And if awoken early, we may still be on the waiter list and must
177 1.71 ad * remove self.
178 1.70 ad *
179 1.71 ad * In all cases do the wakeup without the mutex held otherwise:
180 1.71 ad *
181 1.71 ad * - wakeup could be deferred until mutex release
182 1.71 ad * - it would be mixing up two sets of waitpoints
183 1.71 ad */
184 1.71 ad if (__predict_false(self->pt_condwait | cancel | retval)) {
185 1.71 ad pthread_cond_broadcast(cond);
186 1.71 ad
187 1.71 ad /*
188 1.72 riastrad * Might have raced with another thread to do the wakeup.
189 1.71 ad * In any case there will be a wakeup for sure. Eat it and
190 1.71 ad * wait for pt_condwait to clear.
191 1.71 ad */
192 1.71 ad do {
193 1.71 ad (void)_lwp_park(CLOCK_REALTIME, TIMER_ABSTIME, NULL,
194 1.71 ad 0, NULL, NULL);
195 1.71 ad } while (self->pt_condwait);
196 1.71 ad }
197 1.71 ad
198 1.71 ad /*
199 1.71 ad * If cancelled then exit. POSIX dictates that the mutex must be
200 1.71 ad * held if this happens.
201 1.70 ad */
202 1.70 ad pthread_mutex_lock(mutex);
203 1.71 ad if (cancel) {
204 1.71 ad pthread__cancelled();
205 1.70 ad }
206 1.32 ad
207 1.43 ad return retval;
208 1.2 thorpej }
209 1.2 thorpej
210 1.2 thorpej int
211 1.43 ad pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
212 1.2 thorpej {
213 1.59 christos if (__predict_false(__uselibcstub))
214 1.59 christos return __libc_cond_wait_stub(cond, mutex);
215 1.32 ad
216 1.43 ad return pthread_cond_timedwait(cond, mutex, NULL);
217 1.2 thorpej }
218 1.2 thorpej
219 1.70 ad int
220 1.70 ad pthread_cond_signal(pthread_cond_t *cond)
221 1.2 thorpej {
222 1.70 ad pthread_t self, thread, next;
223 1.28 ad pthread_mutex_t *mutex;
224 1.43 ad
225 1.70 ad if (__predict_false(__uselibcstub))
226 1.70 ad return __libc_cond_signal_stub(cond);
227 1.70 ad
228 1.70 ad pthread__error(EINVAL, "Invalid condition variable",
229 1.70 ad cond->ptc_magic == _PT_COND_MAGIC);
230 1.70 ad
231 1.70 ad /* Take ownership of one waiter. */
232 1.70 ad self = pthread_self();
233 1.28 ad mutex = cond->ptc_mutex;
234 1.70 ad for (thread = cond->ptc_waiters;; thread = next) {
235 1.70 ad if (thread == NULL) {
236 1.70 ad return 0;
237 1.70 ad }
238 1.70 ad membar_datadep_consumer(); /* for alpha */
239 1.70 ad next = thread->pt_condnext;
240 1.70 ad next = atomic_cas_ptr(&cond->ptc_waiters, thread, next);
241 1.70 ad if (__predict_true(next == thread)) {
242 1.70 ad break;
243 1.70 ad }
244 1.43 ad }
245 1.70 ad if (self->pt_nwaiters >= pthread__unpark_max) {
246 1.69 ad pthread__clear_waiters(self);
247 1.4 nathanw }
248 1.70 ad self->pt_waiters[self->pt_nwaiters++] = thread->pt_lid;
249 1.70 ad membar_sync();
250 1.70 ad thread->pt_condwait = 0;
251 1.70 ad /* No longer safe to touch 'thread' */
252 1.43 ad pthread__mutex_deferwake(self, mutex);
253 1.2 thorpej return 0;
254 1.2 thorpej }
255 1.2 thorpej
256 1.2 thorpej int
257 1.70 ad pthread_cond_broadcast(pthread_cond_t *cond)
258 1.49 ad {
259 1.70 ad pthread_t self, thread, next;
260 1.70 ad pthread_mutex_t *mutex;
261 1.49 ad
262 1.59 christos if (__predict_false(__uselibcstub))
263 1.70 ad return __libc_cond_broadcast_stub(cond);
264 1.59 christos
265 1.67 kamil pthread__error(EINVAL, "Invalid condition variable",
266 1.67 kamil cond->ptc_magic == _PT_COND_MAGIC);
267 1.67 kamil
268 1.70 ad if (cond->ptc_waiters == NULL)
269 1.49 ad return 0;
270 1.49 ad
271 1.70 ad /* Take ownership of the current set of waiters. */
272 1.70 ad self = pthread_self();
273 1.70 ad mutex = cond->ptc_mutex;
274 1.70 ad thread = atomic_swap_ptr(&cond->ptc_waiters, NULL);
275 1.70 ad membar_datadep_consumer(); /* for alpha */
276 1.2 thorpej
277 1.28 ad /*
278 1.70 ad * Pull waiters from the queue and add to our list. Use a memory
279 1.70 ad * barrier to ensure that we safely read the value of pt_condnext
280 1.70 ad * before 'thread' sees pt_condwait being cleared.
281 1.28 ad */
282 1.70 ad while (thread != NULL) {
283 1.70 ad if (self->pt_nwaiters >= pthread__unpark_max) {
284 1.69 ad pthread__clear_waiters(self);
285 1.28 ad }
286 1.70 ad next = thread->pt_condnext;
287 1.70 ad self->pt_waiters[self->pt_nwaiters++] = thread->pt_lid;
288 1.70 ad membar_sync();
289 1.70 ad thread->pt_condwait = 0;
290 1.70 ad /* No longer safe to touch 'thread' */
291 1.70 ad thread = next;
292 1.35 ad }
293 1.43 ad pthread__mutex_deferwake(self, mutex);
294 1.2 thorpej return 0;
295 1.2 thorpej }
296 1.2 thorpej
297 1.49 ad int
298 1.51 pooka _pthread_cond_has_waiters_np(pthread_cond_t *cond)
299 1.51 pooka {
300 1.51 pooka
301 1.70 ad return cond->ptc_waiters != NULL;
302 1.51 pooka }
303 1.51 pooka
304 1.51 pooka int
305 1.2 thorpej pthread_condattr_init(pthread_condattr_t *attr)
306 1.2 thorpej {
307 1.2 thorpej
308 1.2 thorpej attr->ptca_magic = _PT_CONDATTR_MAGIC;
309 1.58 christos attr->ptca_private = NULL;
310 1.2 thorpej
311 1.2 thorpej return 0;
312 1.2 thorpej }
313 1.2 thorpej
314 1.2 thorpej int
315 1.58 christos pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clck)
316 1.58 christos {
317 1.67 kamil
318 1.67 kamil pthread__error(EINVAL, "Invalid condition variable attribute",
319 1.67 kamil attr->ptca_magic == _PT_CONDATTR_MAGIC);
320 1.67 kamil
321 1.58 christos switch (clck) {
322 1.58 christos case CLOCK_MONOTONIC:
323 1.58 christos case CLOCK_REALTIME:
324 1.58 christos if (attr->ptca_private == NULL)
325 1.58 christos attr->ptca_private = malloc(sizeof(clockid_t));
326 1.58 christos if (attr->ptca_private == NULL)
327 1.58 christos return errno;
328 1.58 christos *(clockid_t *)attr->ptca_private = clck;
329 1.58 christos return 0;
330 1.58 christos default:
331 1.58 christos return EINVAL;
332 1.58 christos }
333 1.58 christos }
334 1.58 christos
335 1.58 christos int
336 1.64 christos pthread_condattr_getclock(const pthread_condattr_t *__restrict attr,
337 1.64 christos clockid_t *__restrict clock_id)
338 1.64 christos {
339 1.67 kamil
340 1.67 kamil pthread__error(EINVAL, "Invalid condition variable attribute",
341 1.67 kamil attr->ptca_magic == _PT_CONDATTR_MAGIC);
342 1.67 kamil
343 1.64 christos if (attr == NULL || attr->ptca_private == NULL)
344 1.64 christos return EINVAL;
345 1.64 christos *clock_id = *(clockid_t *)attr->ptca_private;
346 1.64 christos return 0;
347 1.64 christos }
348 1.64 christos
349 1.64 christos int
350 1.2 thorpej pthread_condattr_destroy(pthread_condattr_t *attr)
351 1.2 thorpej {
352 1.2 thorpej
353 1.11 nathanw pthread__error(EINVAL, "Invalid condition variable attribute",
354 1.11 nathanw attr->ptca_magic == _PT_CONDATTR_MAGIC);
355 1.2 thorpej
356 1.2 thorpej attr->ptca_magic = _PT_CONDATTR_DEAD;
357 1.58 christos free(attr->ptca_private);
358 1.2 thorpej
359 1.2 thorpej return 0;
360 1.6 nathanw }
361 1.6 nathanw
362 1.64 christos #ifdef _PTHREAD_PSHARED
363 1.64 christos int
364 1.64 christos pthread_condattr_getpshared(const pthread_condattr_t * __restrict attr,
365 1.64 christos int * __restrict pshared)
366 1.64 christos {
367 1.64 christos
368 1.67 kamil pthread__error(EINVAL, "Invalid condition variable attribute",
369 1.67 kamil attr->ptca_magic == _PT_CONDATTR_MAGIC);
370 1.67 kamil
371 1.64 christos *pshared = PTHREAD_PROCESS_PRIVATE;
372 1.64 christos return 0;
373 1.64 christos }
374 1.64 christos
375 1.64 christos int
376 1.64 christos pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared)
377 1.64 christos {
378 1.64 christos
379 1.67 kamil pthread__error(EINVAL, "Invalid condition variable attribute",
380 1.67 kamil attr->ptca_magic == _PT_CONDATTR_MAGIC);
381 1.67 kamil
382 1.64 christos switch(pshared) {
383 1.64 christos case PTHREAD_PROCESS_PRIVATE:
384 1.64 christos return 0;
385 1.64 christos case PTHREAD_PROCESS_SHARED:
386 1.64 christos return ENOSYS;
387 1.64 christos }
388 1.64 christos return EINVAL;
389 1.64 christos }
390 1.64 christos #endif
391