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