/* $OpenBSD: rthread_cond.c,v 1.6 2024/01/10 04:28:43 cheloha Exp $ */ /* * Copyright (c) 2017 Martin Pieuchot * Copyright (c) 2012 Philip Guenther * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "rthread.h" #include "cancel.h" #include "synch.h" int pthread_cond_init(pthread_cond_t *condp, const pthread_condattr_t *attr) { pthread_cond_t cond; cond = calloc(1, sizeof(*cond)); if (cond == NULL) return (ENOMEM); if (attr == NULL) cond->clock = CLOCK_REALTIME; else cond->clock = (*attr)->ca_clock; *condp = cond; return (0); } DEF_STRONG(pthread_cond_init); int pthread_cond_destroy(pthread_cond_t *condp) { pthread_cond_t cond; cond = *condp; if (cond != NULL) { if (cond->mutex != NULL) { #define MSG "pthread_cond_destroy on condvar with waiters!\n" write(2, MSG, sizeof(MSG) - 1); #undef MSG return (EBUSY); } free(cond); } *condp = NULL; return (0); } int _rthread_cond_timedwait(pthread_cond_t cond, pthread_mutex_t *mutexp, const struct timespec *abs) { struct pthread_mutex *mutex = (struct pthread_mutex *)*mutexp; struct tib *tib = TIB_GET(); pthread_t self = tib->tib_thread; int error, rv = 0, canceled = 0, mutex_count = 0; clockid_t clock = cond->clock; int seq = cond->seq; PREP_CANCEL_POINT(tib); _rthread_debug(5, "%p: cond_timed %p,%p (%p)\n", self, (void *)cond, (void *)mutex, (void *)mutex->owner); ENTER_DELAYED_CANCEL_POINT(tib, self); #if notyet /* mark the condvar as being associated with this mutex */ if (cond->mutex == NULL) atomic_cas_ptr(&cond->mutex, NULL, mutex); if (cond->mutex != mutex) { LEAVE_CANCEL_POINT_INNER(tib, 1); return (EINVAL); } #endif /* snag the count in case this is a recursive mutex */ if (mutex->type == PTHREAD_MUTEX_RECURSIVE) mutex_count = mutex->count; pthread_mutex_unlock(mutexp); do { /* If ``seq'' wraps you deserve to lose a signal. */ error = _twait(&cond->seq, seq, clock, abs); /* * If we took a normal signal (not from cancellation) then * we should just go back to sleep without changing state * (timeouts, etc). */ } while ((error == EINTR) && (tib->tib_canceled == 0 || (tib->tib_cantcancel & CANCEL_DISABLED))); /* if timeout or canceled, make note of that */ if (error == ETIMEDOUT) rv = ETIMEDOUT; else if (error == EINTR) canceled = 1; pthread_mutex_lock(mutexp); /* restore the mutex's count */ if (mutex->type == PTHREAD_MUTEX_RECURSIVE) mutex->count = mutex_count; LEAVE_CANCEL_POINT_INNER(tib, canceled); return rv; } int pthread_cond_timedwait(pthread_cond_t *condp, pthread_mutex_t *mutexp, const struct timespec *abs) { pthread_cond_t cond; int error; if (*condp == NULL) { if ((error = pthread_cond_init(condp, NULL))) return (error); } cond = *condp; if (abs == NULL || abs->tv_nsec < 0 || abs->tv_nsec >= 1000000000) return (EINVAL); return (_rthread_cond_timedwait(cond, mutexp, abs)); } int pthread_cond_wait(pthread_cond_t *condp, pthread_mutex_t *mutexp) { pthread_cond_t cond; int error; if (*condp == NULL) { if ((error = pthread_cond_init(condp, NULL))) return (error); } cond = *condp; return (_rthread_cond_timedwait(cond, mutexp, NULL)); } int pthread_cond_signal(pthread_cond_t *condp) { pthread_cond_t cond; int count; if (*condp == NULL) return (0); cond = *condp; atomic_inc_int(&cond->seq); count = _wake(&cond->seq, 1); _rthread_debug(5, "%p: cond_signal %p, %d awaken\n", pthread_self(), (void *)cond, count); return (0); } int pthread_cond_broadcast(pthread_cond_t *condp) { pthread_cond_t cond; int count; if (*condp == NULL) return (0); cond = *condp; atomic_inc_int(&cond->seq); #if notyet count = _requeue(&cond->seq, 1, INT_MAX, &cond->mutex->lock); #else count = _wake(&cond->seq, INT_MAX); #endif _rthread_debug(5, "%p: cond_broadcast %p, %d awaken\n", pthread_self(), (void *)cond, count); return (0); }