1 /* $NetBSD: pthread_cond.c,v 1.81 2026/09/25 23:43:03 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2001, 2006, 2007, 2008, 2020 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Nathan J. Williams and Andrew Doran. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __RCSID("$NetBSD: pthread_cond.c,v 1.81 2026/09/25 23:43:03 christos Exp $"); 34 35 /* Need to use libc-private names for atomic operations. */ 36 #include "../../common/lib/libc/atomic/atomic_op_namespace.h" 37 38 #include <stdatomic.h> 39 #include <stdlib.h> 40 #include <errno.h> 41 #include <sys/time.h> 42 #include <sys/types.h> 43 44 #include "pthread.h" 45 #include "pthread_int.h" 46 #include "reentrant.h" 47 48 #define atomic_load_relaxed(p) \ 49 atomic_load_explicit(p, memory_order_relaxed) 50 51 int _sys___nanosleep50(const struct timespec *, struct timespec *); 52 53 int _pthread_cond_has_waiters_np(pthread_cond_t *); 54 55 __weak_alias(pthread_cond_has_waiters_np,_pthread_cond_has_waiters_np) 56 57 __strong_alias(__libc_condattr_init,pthread_condattr_init) 58 __strong_alias(__libc_condattr_destroy,pthread_condattr_destroy) 59 __strong_alias(__libc_condattr_setclock,pthread_condattr_setclock) 60 61 __strong_alias(__libc_cond_init,pthread_cond_init) 62 __strong_alias(__libc_cond_signal,pthread_cond_signal) 63 __strong_alias(__libc_cond_broadcast,pthread_cond_broadcast) 64 __strong_alias(__libc_cond_wait,pthread_cond_wait) 65 __strong_alias(__libc_cond_timedwait,pthread_cond_timedwait) 66 __strong_alias(__libc_cond_destroy,pthread_cond_destroy) 67 68 /* 69 * A dummy waiter that's used to flag that pthread_cond_signal() is in 70 * progress and nobody else should try to modify the waiter list until 71 * it completes. 72 */ 73 #define pthread__cond_dummy_ptr ((struct pthread__waiter *)1) 74 75 static clockid_t 76 pthread_cond_getclock(const pthread_cond_t *cond) 77 { 78 79 pthread__error(EINVAL, "Invalid condition variable", 80 cond->ptc_magic == _PT_COND_MAGIC); 81 82 return (clockid_t)(intptr_t)cond->ptc_private; 83 } 84 85 int 86 pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) 87 { 88 if (__predict_false(__uselibcstub)) 89 return __libc_cond_init_stub(cond, attr); 90 91 pthread__error(EINVAL, "Invalid condition variable attribute", 92 (attr == NULL) || (attr->ptca_magic == _PT_CONDATTR_MAGIC)); 93 94 cond->ptc_magic = _PT_COND_MAGIC; 95 cond->ptc_waiters = NULL; 96 cond->ptc_mutex = NULL; 97 if (attr) 98 cond->ptc_private = attr->ptca_private; 99 else 100 cond->ptc_private = (void *)CLOCK_REALTIME; 101 102 return 0; 103 } 104 105 106 int 107 pthread_cond_destroy(pthread_cond_t *cond) 108 { 109 if (__predict_false(__uselibcstub)) 110 return __libc_cond_destroy_stub(cond); 111 112 pthread__error(EINVAL, "Invalid condition variable", 113 cond->ptc_magic == _PT_COND_MAGIC); 114 pthread__error(EBUSY, "Destroying condition variable in use", 115 cond->ptc_waiters == NULL); 116 117 cond->ptc_magic = _PT_COND_DEAD; 118 119 return 0; 120 } 121 122 int 123 pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 124 const struct timespec *abstime) 125 { 126 struct pthread__waiter waiter, *next, *head; 127 pthread_t self; 128 int error, cancel; 129 clockid_t clkid = pthread_cond_getclock(cond); 130 131 if (__predict_false(__uselibcstub)) 132 return __libc_cond_timedwait_stub(cond, mutex, abstime); 133 134 pthread__error(EINVAL, "Invalid condition variable", 135 cond->ptc_magic == _PT_COND_MAGIC); 136 pthread__error(EINVAL, "Invalid mutex", 137 mutex->ptm_magic == _PT_MUTEX_MAGIC); 138 pthread__error(EPERM, "Mutex not locked in condition wait", 139 mutex->ptm_owner != NULL); 140 141 self = pthread__self(); 142 pthread__assert(self->pt_lid != 0); 143 144 if (__predict_false(atomic_load_relaxed(&self->pt_cancel) & 145 PT_CANCEL_CANCELLED)) { 146 membar_acquire(); 147 pthread__cancelled(); 148 } 149 150 /* Note this thread as waiting on the CV. */ 151 cond->ptc_mutex = mutex; 152 for (head = cond->ptc_waiters;; head = next) { 153 /* Wait while pthread_cond_signal() in progress. */ 154 if (__predict_false(head == pthread__cond_dummy_ptr)) { 155 sched_yield(); 156 next = cond->ptc_waiters; 157 continue; 158 } 159 waiter.lid = self->pt_lid; 160 waiter.next = head; 161 #ifndef PTHREAD__ATOMIC_IS_MEMBAR 162 membar_producer(); 163 #endif 164 next = atomic_cas_ptr(&cond->ptc_waiters, head, &waiter); 165 if (__predict_true(next == head)) { 166 break; 167 } 168 } 169 170 /* Drop the interlock and wait. */ 171 error = 0; 172 pthread_mutex_unlock(mutex); 173 while (waiter.lid && 174 !(cancel = atomic_load_relaxed(&self->pt_cancel) & 175 PT_CANCEL_CANCELLED)) { 176 int rv = _lwp_park(clkid, TIMER_ABSTIME, __UNCONST(abstime), 177 0, NULL, NULL); 178 if (rv == 0) { 179 continue; 180 } 181 if (errno != EINTR && errno != EALREADY) { 182 error = errno; 183 break; 184 } 185 } 186 pthread_mutex_lock(mutex); 187 188 /* 189 * If this thread absorbed a wakeup from pthread_cond_signal() and 190 * cannot take the wakeup, we should ensure that another thread does. 191 * 192 * And if awoken early, we may still be on the waiter list and must 193 * remove self. 194 */ 195 if (__predict_false(cancel | error)) { 196 pthread_cond_broadcast(cond); 197 198 /* 199 * Might have raced with another thread to do the wakeup. 200 * Wait until released, otherwise "waiter" is still globally 201 * visible. 202 */ 203 pthread_mutex_unlock(mutex); 204 while (__predict_false(waiter.lid)) { 205 (void)_lwp_park(CLOCK_MONOTONIC, 0, NULL, 0, NULL, 206 NULL); 207 } 208 pthread_mutex_lock(mutex); 209 } else { 210 pthread__assert(!waiter.lid); 211 } 212 213 /* 214 * If cancelled then exit. POSIX dictates that the mutex must be 215 * held if this happens. 216 */ 217 if (cancel) { 218 membar_acquire(); 219 pthread__cancelled(); 220 } 221 222 return error; 223 } 224 225 int 226 pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 227 { 228 if (__predict_false(__uselibcstub)) 229 return __libc_cond_wait_stub(cond, mutex); 230 231 return pthread_cond_timedwait(cond, mutex, NULL); 232 } 233 234 int 235 pthread_cond_signal(pthread_cond_t *cond) 236 { 237 struct pthread__waiter *head, *next; 238 pthread_mutex_t *mutex; 239 pthread_t self; 240 241 if (__predict_false(__uselibcstub)) 242 return __libc_cond_signal_stub(cond); 243 244 pthread__error(EINVAL, "Invalid condition variable", 245 cond->ptc_magic == _PT_COND_MAGIC); 246 247 /* Take ownership of one waiter. */ 248 self = pthread_self(); 249 mutex = cond->ptc_mutex; 250 for (head = cond->ptc_waiters;; head = next) { 251 /* Wait while pthread_cond_signal() in progress. */ 252 if (__predict_false(head == pthread__cond_dummy_ptr)) { 253 sched_yield(); 254 next = cond->ptc_waiters; 255 continue; 256 } 257 if (head == NULL) { 258 return 0; 259 } 260 /* Block concurrent access to the waiter list. */ 261 next = atomic_cas_ptr(&cond->ptc_waiters, head, 262 pthread__cond_dummy_ptr); 263 if (__predict_true(next == head)) { 264 break; 265 } 266 } 267 268 /* Now that list is locked, read pointer to next and then unlock. */ 269 membar_enter(); 270 cond->ptc_waiters = head->next; 271 membar_producer(); 272 head->next = NULL; 273 274 /* Now transfer waiter to the mutex. */ 275 pthread__mutex_deferwake(self, mutex, head); 276 return 0; 277 } 278 279 int 280 pthread_cond_broadcast(pthread_cond_t *cond) 281 { 282 struct pthread__waiter *head, *next; 283 pthread_mutex_t *mutex; 284 pthread_t self; 285 286 if (__predict_false(__uselibcstub)) 287 return __libc_cond_broadcast_stub(cond); 288 289 pthread__error(EINVAL, "Invalid condition variable", 290 cond->ptc_magic == _PT_COND_MAGIC); 291 292 if (cond->ptc_waiters == NULL) 293 return 0; 294 295 /* Take ownership of current set of waiters. */ 296 self = pthread_self(); 297 mutex = cond->ptc_mutex; 298 for (head = cond->ptc_waiters;; head = next) { 299 /* Wait while pthread_cond_signal() in progress. */ 300 if (__predict_false(head == pthread__cond_dummy_ptr)) { 301 sched_yield(); 302 next = cond->ptc_waiters; 303 continue; 304 } 305 if (head == NULL) { 306 return 0; 307 } 308 next = atomic_cas_ptr(&cond->ptc_waiters, head, NULL); 309 if (__predict_true(next == head)) { 310 break; 311 } 312 } 313 membar_enter(); 314 315 /* Now transfer waiters to the mutex. */ 316 pthread__mutex_deferwake(self, mutex, head); 317 return 0; 318 } 319 320 int 321 _pthread_cond_has_waiters_np(pthread_cond_t *cond) 322 { 323 324 return cond->ptc_waiters != NULL; 325 } 326 327 int 328 pthread_condattr_init(pthread_condattr_t *attr) 329 { 330 331 attr->ptca_magic = _PT_CONDATTR_MAGIC; 332 attr->ptca_private = (void *)CLOCK_REALTIME; 333 334 return 0; 335 } 336 337 int 338 pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clck) 339 { 340 341 pthread__error(EINVAL, "Invalid condition variable attribute", 342 attr->ptca_magic == _PT_CONDATTR_MAGIC); 343 344 switch (clck) { 345 case CLOCK_MONOTONIC: 346 case CLOCK_REALTIME: 347 attr->ptca_private = (void *)(intptr_t)clck; 348 return 0; 349 default: 350 return EINVAL; 351 } 352 } 353 354 int 355 pthread_condattr_getclock(const pthread_condattr_t *__restrict attr, 356 clockid_t *__restrict clock_id) 357 { 358 359 pthread__error(EINVAL, "Invalid condition variable attribute", 360 attr->ptca_magic == _PT_CONDATTR_MAGIC); 361 362 if (attr == NULL) 363 return EINVAL; 364 *clock_id = (clockid_t)(intptr_t)attr->ptca_private; 365 return 0; 366 } 367 368 int 369 pthread_condattr_destroy(pthread_condattr_t *attr) 370 { 371 372 pthread__error(EINVAL, "Invalid condition variable attribute", 373 attr->ptca_magic == _PT_CONDATTR_MAGIC); 374 375 attr->ptca_magic = _PT_CONDATTR_DEAD; 376 377 return 0; 378 } 379 380 #ifdef _PTHREAD_PSHARED 381 int 382 pthread_condattr_getpshared(const pthread_condattr_t * __restrict attr, 383 int * __restrict pshared) 384 { 385 386 pthread__error(EINVAL, "Invalid condition variable attribute", 387 attr->ptca_magic == _PT_CONDATTR_MAGIC); 388 389 *pshared = PTHREAD_PROCESS_PRIVATE; 390 return 0; 391 } 392 393 int 394 pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared) 395 { 396 397 pthread__error(EINVAL, "Invalid condition variable attribute", 398 attr->ptca_magic == _PT_CONDATTR_MAGIC); 399 400 switch(pshared) { 401 case PTHREAD_PROCESS_PRIVATE: 402 return 0; 403 case PTHREAD_PROCESS_SHARED: 404 return ENOSYS; 405 } 406 return EINVAL; 407 } 408 #endif 409