xref: /openbsd-src/lib/librthread/rthread_rwlock_compat.c (revision c554a768c1617ab8d979dfdbb7ffdf8827e11e8e)
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