1 /* $OpenBSD: rthread_rwlock_compat.c,v 1.1 2019/02/13 13:15:39 mpi Exp $ */ 2 /* 3 * Copyright (c) 2004,2005 Ted Unangst <tedu@openbsd.org> 4 * Copyright (c) 2012 Philip Guenther <guenther@openbsd.org> 5 * All Rights Reserved. 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 /* 20 * rwlocks 21 */ 22 23 #include <assert.h> 24 #include <stdlib.h> 25 #include <unistd.h> 26 #include <errno.h> 27 28 #include <pthread.h> 29 30 #include "rthread.h" 31 32 static _atomic_lock_t rwlock_init_lock = _SPINLOCK_UNLOCKED; 33 34 int 35 pthread_rwlock_init(pthread_rwlock_t *lockp, 36 const pthread_rwlockattr_t *attrp __unused) 37 { 38 pthread_rwlock_t lock; 39 40 lock = calloc(1, sizeof(*lock)); 41 if (!lock) 42 return (errno); 43 lock->lock = _SPINLOCK_UNLOCKED; 44 TAILQ_INIT(&lock->writers); 45 46 *lockp = lock; 47 48 return (0); 49 } 50 DEF_STD(pthread_rwlock_init); 51 52 int 53 pthread_rwlock_destroy(pthread_rwlock_t *lockp) 54 { 55 pthread_rwlock_t lock; 56 57 assert(lockp); 58 lock = *lockp; 59 if (lock) { 60 if (lock->readers || !TAILQ_EMPTY(&lock->writers)) { 61 #define MSG "pthread_rwlock_destroy on rwlock with waiters!\n" 62 write(2, MSG, sizeof(MSG) - 1); 63 #undef MSG 64 return (EBUSY); 65 } 66 free(lock); 67 } 68 *lockp = NULL; 69 70 return (0); 71 } 72 73 static int 74 _rthread_rwlock_ensure_init(pthread_rwlock_t *lockp) 75 { 76 int ret = 0; 77 78 /* 79 * If the rwlock is statically initialized, perform the dynamic 80 * initialization. 81 */ 82 if (*lockp == NULL) 83 { 84 _spinlock(&rwlock_init_lock); 85 if (*lockp == NULL) 86 ret = pthread_rwlock_init(lockp, NULL); 87 _spinunlock(&rwlock_init_lock); 88 } 89 return (ret); 90 } 91 92 93 static int 94 _rthread_rwlock_rdlock(pthread_rwlock_t *lockp, const struct timespec *abstime, 95 int try) 96 { 97 pthread_rwlock_t lock; 98 pthread_t thread = pthread_self(); 99 int error; 100 101 if ((error = _rthread_rwlock_ensure_init(lockp))) 102 return (error); 103 104 lock = *lockp; 105 _rthread_debug(5, "%p: rwlock_rdlock %p\n", (void *)thread, 106 (void *)lock); 107 _spinlock(&lock->lock); 108 109 /* writers have precedence */ 110 if (lock->owner == NULL && TAILQ_EMPTY(&lock->writers)) 111 lock->readers++; 112 else if (try) 113 error = EBUSY; 114 else if (lock->owner == thread) 115 error = EDEADLK; 116 else { 117 do { 118 if (__thrsleep(lock, CLOCK_REALTIME, abstime, 119 &lock->lock, NULL) == EWOULDBLOCK) 120 return (ETIMEDOUT); 121 _spinlock(&lock->lock); 122 } while (lock->owner != NULL || !TAILQ_EMPTY(&lock->writers)); 123 lock->readers++; 124 } 125 _spinunlock(&lock->lock); 126 127 return (error); 128 } 129 130 int 131 pthread_rwlock_rdlock(pthread_rwlock_t *lockp) 132 { 133 return (_rthread_rwlock_rdlock(lockp, NULL, 0)); 134 } 135 136 int 137 pthread_rwlock_tryrdlock(pthread_rwlock_t *lockp) 138 { 139 return (_rthread_rwlock_rdlock(lockp, NULL, 1)); 140 } 141 142 int 143 pthread_rwlock_timedrdlock(pthread_rwlock_t *lockp, 144 const struct timespec *abstime) 145 { 146 if (abstime == NULL || abstime->tv_nsec < 0 || 147 abstime->tv_nsec >= 1000000000) 148 return (EINVAL); 149 return (_rthread_rwlock_rdlock(lockp, abstime, 0)); 150 } 151 152 153 static int 154 _rthread_rwlock_wrlock(pthread_rwlock_t *lockp, const struct timespec *abstime, 155 int try) 156 { 157 pthread_rwlock_t lock; 158 pthread_t thread = pthread_self(); 159 int error; 160 161 if ((error = _rthread_rwlock_ensure_init(lockp))) 162 return (error); 163 164 lock = *lockp; 165 166 _rthread_debug(5, "%p: rwlock_timedwrlock %p\n", (void *)thread, 167 (void *)lock); 168 _spinlock(&lock->lock); 169 if (lock->readers == 0 && lock->owner == NULL) 170 lock->owner = thread; 171 else if (try) 172 error = EBUSY; 173 else if (lock->owner == thread) 174 error = EDEADLK; 175 else { 176 int do_wait; 177 178 /* gotta block */ 179 TAILQ_INSERT_TAIL(&lock->writers, thread, waiting); 180 do { 181 do_wait = __thrsleep(thread, CLOCK_REALTIME, abstime, 182 &lock->lock, NULL) != EWOULDBLOCK; 183 _spinlock(&lock->lock); 184 } while (lock->owner != thread && do_wait); 185 186 if (lock->owner != thread) { 187 /* timed out, sigh */ 188 TAILQ_REMOVE(&lock->writers, thread, waiting); 189 error = ETIMEDOUT; 190 } 191 } 192 _spinunlock(&lock->lock); 193 194 return (error); 195 } 196 197 int 198 pthread_rwlock_wrlock(pthread_rwlock_t *lockp) 199 { 200 return (_rthread_rwlock_wrlock(lockp, NULL, 0)); 201 } 202 203 int 204 pthread_rwlock_trywrlock(pthread_rwlock_t *lockp) 205 { 206 return (_rthread_rwlock_wrlock(lockp, NULL, 1)); 207 } 208 209 int 210 pthread_rwlock_timedwrlock(pthread_rwlock_t *lockp, 211 const struct timespec *abstime) 212 { 213 if (abstime == NULL || abstime->tv_nsec < 0 || 214 abstime->tv_nsec >= 1000000000) 215 return (EINVAL); 216 return (_rthread_rwlock_wrlock(lockp, abstime, 0)); 217 } 218 219 220 int 221 pthread_rwlock_unlock(pthread_rwlock_t *lockp) 222 { 223 pthread_rwlock_t lock; 224 pthread_t thread = pthread_self(); 225 pthread_t next; 226 int was_writer; 227 228 lock = *lockp; 229 230 _rthread_debug(5, "%p: rwlock_unlock %p\n", (void *)thread, 231 (void *)lock); 232 _spinlock(&lock->lock); 233 if (lock->owner != NULL) { 234 assert(lock->owner == thread); 235 was_writer = 1; 236 } else { 237 assert(lock->readers > 0); 238 lock->readers--; 239 if (lock->readers > 0) 240 goto out; 241 was_writer = 0; 242 } 243 244 lock->owner = next = TAILQ_FIRST(&lock->writers); 245 if (next != NULL) { 246 /* dequeue and wake first writer */ 247 TAILQ_REMOVE(&lock->writers, next, waiting); 248 _spinunlock(&lock->lock); 249 __thrwakeup(next, 1); 250 return (0); 251 } 252 253 /* could there have been blocked readers? wake them all */ 254 if (was_writer) 255 __thrwakeup(lock, 0); 256 out: 257 _spinunlock(&lock->lock); 258 259 return (0); 260 } 261