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