summaryrefslogtreecommitdiff
path: root/lib/librthread/rthread_rwlock_compat.c
diff options
context:
space:
mode:
authorMartin Pieuchot <mpi@cvs.openbsd.org>2019-02-13 13:15:40 +0000
committerMartin Pieuchot <mpi@cvs.openbsd.org>2019-02-13 13:15:40 +0000
commit48c89f1b00bc535a347a506d79f0b9a873851a09 (patch)
tree16ed7be7cd84a0e5ec84021e66114477a1d65ac9 /lib/librthread/rthread_rwlock_compat.c
parent295ef00fa202c71085db039b4553582e2e21dbba (diff)
Import the existing rwlock implementation for architectures that cannot
use the futex(2)-based one due to missing atomic primitives.
Diffstat (limited to 'lib/librthread/rthread_rwlock_compat.c')
-rw-r--r--lib/librthread/rthread_rwlock_compat.c260
1 files changed, 260 insertions, 0 deletions
diff --git a/lib/librthread/rthread_rwlock_compat.c b/lib/librthread/rthread_rwlock_compat.c
new file mode 100644
index 00000000000..f9dc6f858b2
--- /dev/null
+++ b/lib/librthread/rthread_rwlock_compat.c
@@ -0,0 +1,260 @@
+/* $OpenBSD: rthread_rwlock_compat.c,v 1.1 2019/02/13 13:15:39 mpi Exp $ */
+/*
+ * Copyright (c) 2004,2005 Ted Unangst <tedu@openbsd.org>
+ * Copyright (c) 2012 Philip Guenther <guenther@openbsd.org>
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+/*
+ * rwlocks
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <pthread.h>
+
+#include "rthread.h"
+
+static _atomic_lock_t rwlock_init_lock = _SPINLOCK_UNLOCKED;
+
+int
+pthread_rwlock_init(pthread_rwlock_t *lockp,
+ const pthread_rwlockattr_t *attrp __unused)
+{
+ pthread_rwlock_t lock;
+
+ lock = calloc(1, sizeof(*lock));
+ if (!lock)
+ return (errno);
+ lock->lock = _SPINLOCK_UNLOCKED;
+ TAILQ_INIT(&lock->writers);
+
+ *lockp = lock;
+
+ return (0);
+}
+DEF_STD(pthread_rwlock_init);
+
+int
+pthread_rwlock_destroy(pthread_rwlock_t *lockp)
+{
+ pthread_rwlock_t lock;
+
+ assert(lockp);
+ lock = *lockp;
+ if (lock) {
+ if (lock->readers || !TAILQ_EMPTY(&lock->writers)) {
+#define MSG "pthread_rwlock_destroy on rwlock with waiters!\n"
+ write(2, MSG, sizeof(MSG) - 1);
+#undef MSG
+ return (EBUSY);
+ }
+ free(lock);
+ }
+ *lockp = NULL;
+
+ return (0);
+}
+
+static int
+_rthread_rwlock_ensure_init(pthread_rwlock_t *lockp)
+{
+ int ret = 0;
+
+ /*
+ * If the rwlock is statically initialized, perform the dynamic
+ * initialization.
+ */
+ if (*lockp == NULL)
+ {
+ _spinlock(&rwlock_init_lock);
+ if (*lockp == NULL)
+ ret = pthread_rwlock_init(lockp, NULL);
+ _spinunlock(&rwlock_init_lock);
+ }
+ return (ret);
+}
+
+
+static int
+_rthread_rwlock_rdlock(pthread_rwlock_t *lockp, const struct timespec *abstime,
+ int try)
+{
+ pthread_rwlock_t lock;
+ pthread_t thread = pthread_self();
+ int error;
+
+ if ((error = _rthread_rwlock_ensure_init(lockp)))
+ return (error);
+
+ lock = *lockp;
+ _rthread_debug(5, "%p: rwlock_rdlock %p\n", (void *)thread,
+ (void *)lock);
+ _spinlock(&lock->lock);
+
+ /* writers have precedence */
+ if (lock->owner == NULL && TAILQ_EMPTY(&lock->writers))
+ lock->readers++;
+ else if (try)
+ error = EBUSY;
+ else if (lock->owner == thread)
+ error = EDEADLK;
+ else {
+ do {
+ if (__thrsleep(lock, CLOCK_REALTIME, abstime,
+ &lock->lock, NULL) == EWOULDBLOCK)
+ return (ETIMEDOUT);
+ _spinlock(&lock->lock);
+ } while (lock->owner != NULL || !TAILQ_EMPTY(&lock->writers));
+ lock->readers++;
+ }
+ _spinunlock(&lock->lock);
+
+ return (error);
+}
+
+int
+pthread_rwlock_rdlock(pthread_rwlock_t *lockp)
+{
+ return (_rthread_rwlock_rdlock(lockp, NULL, 0));
+}
+
+int
+pthread_rwlock_tryrdlock(pthread_rwlock_t *lockp)
+{
+ return (_rthread_rwlock_rdlock(lockp, NULL, 1));
+}
+
+int
+pthread_rwlock_timedrdlock(pthread_rwlock_t *lockp,
+ const struct timespec *abstime)
+{
+ if (abstime == NULL || abstime->tv_nsec < 0 ||
+ abstime->tv_nsec >= 1000000000)
+ return (EINVAL);
+ return (_rthread_rwlock_rdlock(lockp, abstime, 0));
+}
+
+
+static int
+_rthread_rwlock_wrlock(pthread_rwlock_t *lockp, const struct timespec *abstime,
+ int try)
+{
+ pthread_rwlock_t lock;
+ pthread_t thread = pthread_self();
+ int error;
+
+ if ((error = _rthread_rwlock_ensure_init(lockp)))
+ return (error);
+
+ lock = *lockp;
+
+ _rthread_debug(5, "%p: rwlock_timedwrlock %p\n", (void *)thread,
+ (void *)lock);
+ _spinlock(&lock->lock);
+ if (lock->readers == 0 && lock->owner == NULL)
+ lock->owner = thread;
+ else if (try)
+ error = EBUSY;
+ else if (lock->owner == thread)
+ error = EDEADLK;
+ else {
+ int do_wait;
+
+ /* gotta block */
+ TAILQ_INSERT_TAIL(&lock->writers, thread, waiting);
+ do {
+ do_wait = __thrsleep(thread, CLOCK_REALTIME, abstime,
+ &lock->lock, NULL) != EWOULDBLOCK;
+ _spinlock(&lock->lock);
+ } while (lock->owner != thread && do_wait);
+
+ if (lock->owner != thread) {
+ /* timed out, sigh */
+ TAILQ_REMOVE(&lock->writers, thread, waiting);
+ error = ETIMEDOUT;
+ }
+ }
+ _spinunlock(&lock->lock);
+
+ return (error);
+}
+
+int
+pthread_rwlock_wrlock(pthread_rwlock_t *lockp)
+{
+ return (_rthread_rwlock_wrlock(lockp, NULL, 0));
+}
+
+int
+pthread_rwlock_trywrlock(pthread_rwlock_t *lockp)
+{
+ return (_rthread_rwlock_wrlock(lockp, NULL, 1));
+}
+
+int
+pthread_rwlock_timedwrlock(pthread_rwlock_t *lockp,
+ const struct timespec *abstime)
+{
+ if (abstime == NULL || abstime->tv_nsec < 0 ||
+ abstime->tv_nsec >= 1000000000)
+ return (EINVAL);
+ return (_rthread_rwlock_wrlock(lockp, abstime, 0));
+}
+
+
+int
+pthread_rwlock_unlock(pthread_rwlock_t *lockp)
+{
+ pthread_rwlock_t lock;
+ pthread_t thread = pthread_self();
+ pthread_t next;
+ int was_writer;
+
+ lock = *lockp;
+
+ _rthread_debug(5, "%p: rwlock_unlock %p\n", (void *)thread,
+ (void *)lock);
+ _spinlock(&lock->lock);
+ if (lock->owner != NULL) {
+ assert(lock->owner == thread);
+ was_writer = 1;
+ } else {
+ assert(lock->readers > 0);
+ lock->readers--;
+ if (lock->readers > 0)
+ goto out;
+ was_writer = 0;
+ }
+
+ lock->owner = next = TAILQ_FIRST(&lock->writers);
+ if (next != NULL) {
+ /* dequeue and wake first writer */
+ TAILQ_REMOVE(&lock->writers, next, waiting);
+ _spinunlock(&lock->lock);
+ __thrwakeup(next, 1);
+ return (0);
+ }
+
+ /* could there have been blocked readers? wake them all */
+ if (was_writer)
+ __thrwakeup(lock, 0);
+out:
+ _spinunlock(&lock->lock);
+
+ return (0);
+}