diff options
Diffstat (limited to 'lib/libc/thread/rthread_cond.c')
-rw-r--r-- | lib/libc/thread/rthread_cond.c | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/lib/libc/thread/rthread_cond.c b/lib/libc/thread/rthread_cond.c new file mode 100644 index 00000000000..28c4be3fdc3 --- /dev/null +++ b/lib/libc/thread/rthread_cond.c @@ -0,0 +1,216 @@ +/* $OpenBSD: rthread_cond.c,v 1.1 2017/08/15 06:13:24 guenther Exp $ */ +/* + * Copyright (c) 2017 Martin Pieuchot <mpi@openbsd.org> + * Copyright (c) 2012 Philip Guenther <guenther@openbsd.org> + * + * 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 <assert.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include <pthread.h> + +#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_STD(pthread_cond_init); + +int +pthread_cond_destroy(pthread_cond_t *condp) +{ + pthread_cond_t cond; + + assert(condp != NULL); + 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); +} +DEF_STD(pthread_cond_destroy); + +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_sec < 0 || 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)); +} +DEF_STD(pthread_cond_wait); + +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); +} +DEF_STD(pthread_cond_signal); + +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); +} +DEF_STD(pthread_cond_broadcast); |