Home | History | Annotate | Line # | Download | only in libpthread
      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