summaryrefslogtreecommitdiffstats
path: root/lib/libc/thread/rthread_cond.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libc/thread/rthread_cond.c')
-rw-r--r--lib/libc/thread/rthread_cond.c216
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);