1*c554a768Scheloha /* $OpenBSD: rthread_rwlock_compat.c,v 1.2 2022/05/14 14:52:20 cheloha Exp $ */
20d663b4dSmpi /*
30d663b4dSmpi * Copyright (c) 2004,2005 Ted Unangst <tedu@openbsd.org>
40d663b4dSmpi * Copyright (c) 2012 Philip Guenther <guenther@openbsd.org>
50d663b4dSmpi * All Rights Reserved.
60d663b4dSmpi *
70d663b4dSmpi * Permission to use, copy, modify, and distribute this software for any
80d663b4dSmpi * purpose with or without fee is hereby granted, provided that the above
90d663b4dSmpi * copyright notice and this permission notice appear in all copies.
100d663b4dSmpi *
110d663b4dSmpi * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
120d663b4dSmpi * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
130d663b4dSmpi * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
140d663b4dSmpi * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
150d663b4dSmpi * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
160d663b4dSmpi * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
170d663b4dSmpi * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
180d663b4dSmpi */
190d663b4dSmpi /*
200d663b4dSmpi * rwlocks
210d663b4dSmpi */
220d663b4dSmpi
230d663b4dSmpi #include <assert.h>
240d663b4dSmpi #include <stdlib.h>
250d663b4dSmpi #include <unistd.h>
260d663b4dSmpi #include <errno.h>
270d663b4dSmpi
280d663b4dSmpi #include <pthread.h>
290d663b4dSmpi
300d663b4dSmpi #include "rthread.h"
310d663b4dSmpi
320d663b4dSmpi static _atomic_lock_t rwlock_init_lock = _SPINLOCK_UNLOCKED;
330d663b4dSmpi
340d663b4dSmpi int
pthread_rwlock_init(pthread_rwlock_t * lockp,const pthread_rwlockattr_t * attrp __unused)350d663b4dSmpi pthread_rwlock_init(pthread_rwlock_t *lockp,
360d663b4dSmpi const pthread_rwlockattr_t *attrp __unused)
370d663b4dSmpi {
380d663b4dSmpi pthread_rwlock_t lock;
390d663b4dSmpi
400d663b4dSmpi lock = calloc(1, sizeof(*lock));
410d663b4dSmpi if (!lock)
420d663b4dSmpi return (errno);
430d663b4dSmpi lock->lock = _SPINLOCK_UNLOCKED;
440d663b4dSmpi TAILQ_INIT(&lock->writers);
450d663b4dSmpi
460d663b4dSmpi *lockp = lock;
470d663b4dSmpi
480d663b4dSmpi return (0);
490d663b4dSmpi }
500d663b4dSmpi DEF_STD(pthread_rwlock_init);
510d663b4dSmpi
520d663b4dSmpi int
pthread_rwlock_destroy(pthread_rwlock_t * lockp)530d663b4dSmpi pthread_rwlock_destroy(pthread_rwlock_t *lockp)
540d663b4dSmpi {
550d663b4dSmpi pthread_rwlock_t lock;
560d663b4dSmpi
570d663b4dSmpi assert(lockp);
580d663b4dSmpi lock = *lockp;
590d663b4dSmpi if (lock) {
600d663b4dSmpi if (lock->readers || !TAILQ_EMPTY(&lock->writers)) {
610d663b4dSmpi #define MSG "pthread_rwlock_destroy on rwlock with waiters!\n"
620d663b4dSmpi write(2, MSG, sizeof(MSG) - 1);
630d663b4dSmpi #undef MSG
640d663b4dSmpi return (EBUSY);
650d663b4dSmpi }
660d663b4dSmpi free(lock);
670d663b4dSmpi }
680d663b4dSmpi *lockp = NULL;
690d663b4dSmpi
700d663b4dSmpi return (0);
710d663b4dSmpi }
720d663b4dSmpi
730d663b4dSmpi static int
_rthread_rwlock_ensure_init(pthread_rwlock_t * lockp)740d663b4dSmpi _rthread_rwlock_ensure_init(pthread_rwlock_t *lockp)
750d663b4dSmpi {
760d663b4dSmpi int ret = 0;
770d663b4dSmpi
780d663b4dSmpi /*
790d663b4dSmpi * If the rwlock is statically initialized, perform the dynamic
800d663b4dSmpi * initialization.
810d663b4dSmpi */
820d663b4dSmpi if (*lockp == NULL)
830d663b4dSmpi {
840d663b4dSmpi _spinlock(&rwlock_init_lock);
850d663b4dSmpi if (*lockp == NULL)
860d663b4dSmpi ret = pthread_rwlock_init(lockp, NULL);
870d663b4dSmpi _spinunlock(&rwlock_init_lock);
880d663b4dSmpi }
890d663b4dSmpi return (ret);
900d663b4dSmpi }
910d663b4dSmpi
920d663b4dSmpi
930d663b4dSmpi static int
_rthread_rwlock_rdlock(pthread_rwlock_t * lockp,const struct timespec * abstime,int try)940d663b4dSmpi _rthread_rwlock_rdlock(pthread_rwlock_t *lockp, const struct timespec *abstime,
950d663b4dSmpi int try)
960d663b4dSmpi {
970d663b4dSmpi pthread_rwlock_t lock;
980d663b4dSmpi pthread_t thread = pthread_self();
990d663b4dSmpi int error;
1000d663b4dSmpi
1010d663b4dSmpi if ((error = _rthread_rwlock_ensure_init(lockp)))
1020d663b4dSmpi return (error);
1030d663b4dSmpi
1040d663b4dSmpi lock = *lockp;
1050d663b4dSmpi _rthread_debug(5, "%p: rwlock_rdlock %p\n", (void *)thread,
1060d663b4dSmpi (void *)lock);
1070d663b4dSmpi _spinlock(&lock->lock);
1080d663b4dSmpi
1090d663b4dSmpi /* writers have precedence */
1100d663b4dSmpi if (lock->owner == NULL && TAILQ_EMPTY(&lock->writers))
1110d663b4dSmpi lock->readers++;
1120d663b4dSmpi else if (try)
1130d663b4dSmpi error = EBUSY;
1140d663b4dSmpi else if (lock->owner == thread)
1150d663b4dSmpi error = EDEADLK;
1160d663b4dSmpi else {
1170d663b4dSmpi do {
1180d663b4dSmpi if (__thrsleep(lock, CLOCK_REALTIME, abstime,
1190d663b4dSmpi &lock->lock, NULL) == EWOULDBLOCK)
1200d663b4dSmpi return (ETIMEDOUT);
1210d663b4dSmpi _spinlock(&lock->lock);
1220d663b4dSmpi } while (lock->owner != NULL || !TAILQ_EMPTY(&lock->writers));
1230d663b4dSmpi lock->readers++;
1240d663b4dSmpi }
1250d663b4dSmpi _spinunlock(&lock->lock);
1260d663b4dSmpi
1270d663b4dSmpi return (error);
1280d663b4dSmpi }
1290d663b4dSmpi
1300d663b4dSmpi int
pthread_rwlock_rdlock(pthread_rwlock_t * lockp)1310d663b4dSmpi pthread_rwlock_rdlock(pthread_rwlock_t *lockp)
1320d663b4dSmpi {
1330d663b4dSmpi return (_rthread_rwlock_rdlock(lockp, NULL, 0));
1340d663b4dSmpi }
1350d663b4dSmpi
1360d663b4dSmpi int
pthread_rwlock_tryrdlock(pthread_rwlock_t * lockp)1370d663b4dSmpi pthread_rwlock_tryrdlock(pthread_rwlock_t *lockp)
1380d663b4dSmpi {
1390d663b4dSmpi return (_rthread_rwlock_rdlock(lockp, NULL, 1));
1400d663b4dSmpi }
1410d663b4dSmpi
1420d663b4dSmpi int
pthread_rwlock_timedrdlock(pthread_rwlock_t * lockp,const struct timespec * abstime)1430d663b4dSmpi pthread_rwlock_timedrdlock(pthread_rwlock_t *lockp,
1440d663b4dSmpi const struct timespec *abstime)
1450d663b4dSmpi {
146*c554a768Scheloha if (abstime == NULL || !timespecisvalid(abstime))
1470d663b4dSmpi return (EINVAL);
1480d663b4dSmpi return (_rthread_rwlock_rdlock(lockp, abstime, 0));
1490d663b4dSmpi }
1500d663b4dSmpi
1510d663b4dSmpi
1520d663b4dSmpi static int
_rthread_rwlock_wrlock(pthread_rwlock_t * lockp,const struct timespec * abstime,int try)1530d663b4dSmpi _rthread_rwlock_wrlock(pthread_rwlock_t *lockp, const struct timespec *abstime,
1540d663b4dSmpi int try)
1550d663b4dSmpi {
1560d663b4dSmpi pthread_rwlock_t lock;
1570d663b4dSmpi pthread_t thread = pthread_self();
1580d663b4dSmpi int error;
1590d663b4dSmpi
1600d663b4dSmpi if ((error = _rthread_rwlock_ensure_init(lockp)))
1610d663b4dSmpi return (error);
1620d663b4dSmpi
1630d663b4dSmpi lock = *lockp;
1640d663b4dSmpi
1650d663b4dSmpi _rthread_debug(5, "%p: rwlock_timedwrlock %p\n", (void *)thread,
1660d663b4dSmpi (void *)lock);
1670d663b4dSmpi _spinlock(&lock->lock);
1680d663b4dSmpi if (lock->readers == 0 && lock->owner == NULL)
1690d663b4dSmpi lock->owner = thread;
1700d663b4dSmpi else if (try)
1710d663b4dSmpi error = EBUSY;
1720d663b4dSmpi else if (lock->owner == thread)
1730d663b4dSmpi error = EDEADLK;
1740d663b4dSmpi else {
1750d663b4dSmpi int do_wait;
1760d663b4dSmpi
1770d663b4dSmpi /* gotta block */
1780d663b4dSmpi TAILQ_INSERT_TAIL(&lock->writers, thread, waiting);
1790d663b4dSmpi do {
1800d663b4dSmpi do_wait = __thrsleep(thread, CLOCK_REALTIME, abstime,
1810d663b4dSmpi &lock->lock, NULL) != EWOULDBLOCK;
1820d663b4dSmpi _spinlock(&lock->lock);
1830d663b4dSmpi } while (lock->owner != thread && do_wait);
1840d663b4dSmpi
1850d663b4dSmpi if (lock->owner != thread) {
1860d663b4dSmpi /* timed out, sigh */
1870d663b4dSmpi TAILQ_REMOVE(&lock->writers, thread, waiting);
1880d663b4dSmpi error = ETIMEDOUT;
1890d663b4dSmpi }
1900d663b4dSmpi }
1910d663b4dSmpi _spinunlock(&lock->lock);
1920d663b4dSmpi
1930d663b4dSmpi return (error);
1940d663b4dSmpi }
1950d663b4dSmpi
1960d663b4dSmpi int
pthread_rwlock_wrlock(pthread_rwlock_t * lockp)1970d663b4dSmpi pthread_rwlock_wrlock(pthread_rwlock_t *lockp)
1980d663b4dSmpi {
1990d663b4dSmpi return (_rthread_rwlock_wrlock(lockp, NULL, 0));
2000d663b4dSmpi }
2010d663b4dSmpi
2020d663b4dSmpi int
pthread_rwlock_trywrlock(pthread_rwlock_t * lockp)2030d663b4dSmpi pthread_rwlock_trywrlock(pthread_rwlock_t *lockp)
2040d663b4dSmpi {
2050d663b4dSmpi return (_rthread_rwlock_wrlock(lockp, NULL, 1));
2060d663b4dSmpi }
2070d663b4dSmpi
2080d663b4dSmpi int
pthread_rwlock_timedwrlock(pthread_rwlock_t * lockp,const struct timespec * abstime)2090d663b4dSmpi pthread_rwlock_timedwrlock(pthread_rwlock_t *lockp,
2100d663b4dSmpi const struct timespec *abstime)
2110d663b4dSmpi {
212*c554a768Scheloha if (abstime == NULL || !timespecisvalid(abstime))
2130d663b4dSmpi return (EINVAL);
2140d663b4dSmpi return (_rthread_rwlock_wrlock(lockp, abstime, 0));
2150d663b4dSmpi }
2160d663b4dSmpi
2170d663b4dSmpi
2180d663b4dSmpi int
pthread_rwlock_unlock(pthread_rwlock_t * lockp)2190d663b4dSmpi pthread_rwlock_unlock(pthread_rwlock_t *lockp)
2200d663b4dSmpi {
2210d663b4dSmpi pthread_rwlock_t lock;
2220d663b4dSmpi pthread_t thread = pthread_self();
2230d663b4dSmpi pthread_t next;
2240d663b4dSmpi int was_writer;
2250d663b4dSmpi
2260d663b4dSmpi lock = *lockp;
2270d663b4dSmpi
2280d663b4dSmpi _rthread_debug(5, "%p: rwlock_unlock %p\n", (void *)thread,
2290d663b4dSmpi (void *)lock);
2300d663b4dSmpi _spinlock(&lock->lock);
2310d663b4dSmpi if (lock->owner != NULL) {
2320d663b4dSmpi assert(lock->owner == thread);
2330d663b4dSmpi was_writer = 1;
2340d663b4dSmpi } else {
2350d663b4dSmpi assert(lock->readers > 0);
2360d663b4dSmpi lock->readers--;
2370d663b4dSmpi if (lock->readers > 0)
2380d663b4dSmpi goto out;
2390d663b4dSmpi was_writer = 0;
2400d663b4dSmpi }
2410d663b4dSmpi
2420d663b4dSmpi lock->owner = next = TAILQ_FIRST(&lock->writers);
2430d663b4dSmpi if (next != NULL) {
2440d663b4dSmpi /* dequeue and wake first writer */
2450d663b4dSmpi TAILQ_REMOVE(&lock->writers, next, waiting);
2460d663b4dSmpi _spinunlock(&lock->lock);
2470d663b4dSmpi __thrwakeup(next, 1);
2480d663b4dSmpi return (0);
2490d663b4dSmpi }
2500d663b4dSmpi
2510d663b4dSmpi /* could there have been blocked readers? wake them all */
2520d663b4dSmpi if (was_writer)
2530d663b4dSmpi __thrwakeup(lock, 0);
2540d663b4dSmpi out:
2550d663b4dSmpi _spinunlock(&lock->lock);
2560d663b4dSmpi
2570d663b4dSmpi return (0);
2580d663b4dSmpi }
259