thr_rwlock.c revision 172491
1155324Simp/*- 2155324Simp * Copyright (c) 1998 Alex Nash 3155324Simp * All rights reserved. 4155324Simp * 5155324Simp * Redistribution and use in source and binary forms, with or without 6155324Simp * modification, are permitted provided that the following conditions 7155324Simp * are met: 8155324Simp * 1. Redistributions of source code must retain the above copyright 9155324Simp * notice, this list of conditions and the following disclaimer. 10155324Simp * 2. Redistributions in binary form must reproduce the above copyright 11155324Simp * notice, this list of conditions and the following disclaimer in the 12155324Simp * documentation and/or other materials provided with the distribution. 13155324Simp * 14155324Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15155324Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16155324Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17155324Simp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18155324Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19155324Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20155324Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21155324Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22155324Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23155324Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24155324Simp * SUCH DAMAGE. 25155324Simp * 26155324Simp * $FreeBSD: head/lib/libkse/thread/thr_rwlock.c 156611 2006-03-13 00:59:51Z deischen $ 27155324Simp */ 28155324Simp 29155324Simp#include <errno.h> 30155324Simp#include <limits.h> 31155324Simp#include <stdlib.h> 32155324Simp 33155324Simp#include "namespace.h" 34155324Simp#include <pthread.h> 35155324Simp#include "un-namespace.h" 36155324Simp#include "thr_private.h" 37155324Simp 38155324Simp/* maximum number of times a read lock may be obtained */ 39155324Simp#define MAX_READ_LOCKS (INT_MAX - 1) 40155324Simp 41155324SimpLT10_COMPAT_PRIVATE(_pthread_rwlock_destroy); 42155324SimpLT10_COMPAT_DEFAULT(pthread_rwlock_destroy); 43155324SimpLT10_COMPAT_PRIVATE(_pthread_rwlock_init); 44155324SimpLT10_COMPAT_DEFAULT(pthread_rwlock_init); 45155324SimpLT10_COMPAT_PRIVATE(_pthread_rwlock_rdlock); 46155324SimpLT10_COMPAT_DEFAULT(pthread_rwlock_rdlock); 47155324SimpLT10_COMPAT_PRIVATE(_pthread_rwlock_timedrdlock); 48155324SimpLT10_COMPAT_DEFAULT(pthread_rwlock_timedrdlock); 49155324SimpLT10_COMPAT_PRIVATE(_pthread_rwlock_tryrdlock); 50155324SimpLT10_COMPAT_DEFAULT(pthread_rwlock_tryrdlock); 51155324SimpLT10_COMPAT_PRIVATE(_pthread_rwlock_trywrlock); 52155324SimpLT10_COMPAT_DEFAULT(pthread_rwlock_trywrlock); 53155324SimpLT10_COMPAT_PRIVATE(_pthread_rwlock_unlock); 54155324SimpLT10_COMPAT_DEFAULT(pthread_rwlock_unlock); 55155324SimpLT10_COMPAT_PRIVATE(_pthread_rwlock_wrlock); 56155324SimpLT10_COMPAT_DEFAULT(pthread_rwlock_wrlock); 57161704ScognetLT10_COMPAT_PRIVATE(_pthread_rwlock_timedwrlock); 58161704ScognetLT10_COMPAT_DEFAULT(pthread_rwlock_timedwrlock); 59155324Simp 60161704Scognet__weak_reference(_pthread_rwlock_destroy, pthread_rwlock_destroy); 61161704Scognet__weak_reference(_pthread_rwlock_init, pthread_rwlock_init); 62161704Scognet__weak_reference(_pthread_rwlock_rdlock, pthread_rwlock_rdlock); 63155324Simp__weak_reference(_pthread_rwlock_timedrdlock, pthread_rwlock_timedrdlock); 64155324Simp__weak_reference(_pthread_rwlock_tryrdlock, pthread_rwlock_tryrdlock); 65155324Simp__weak_reference(_pthread_rwlock_trywrlock, pthread_rwlock_trywrlock); 66155324Simp__weak_reference(_pthread_rwlock_unlock, pthread_rwlock_unlock); 67155324Simp__weak_reference(_pthread_rwlock_wrlock, pthread_rwlock_wrlock); 68155324Simp__weak_reference(_pthread_rwlock_timedwrlock, pthread_rwlock_timedwrlock); 69155324Simp 70155324Simp/* 71155324Simp * Prototypes 72155324Simp */ 73155324Simpstatic int init_static(pthread_rwlock_t *rwlock); 74155324Simp 75155324Simp 76155324Simpstatic int 77155324Simpinit_static(pthread_rwlock_t *rwlock) 78155324Simp{ 79155324Simp struct pthread *thread = _get_curthread(); 80155324Simp int ret; 81155324Simp 82155324Simp THR_LOCK_ACQUIRE(thread, &_rwlock_static_lock); 83155324Simp 84155324Simp if (*rwlock == NULL) 85155324Simp ret = _pthread_rwlock_init(rwlock, NULL); 86155324Simp else 87155324Simp ret = 0; 88155324Simp 89155324Simp THR_LOCK_RELEASE(thread, &_rwlock_static_lock); 90155324Simp return (ret); 91156828Simp} 92156828Simp 93156828Simpint 94156828Simp_pthread_rwlock_destroy (pthread_rwlock_t *rwlock) 95156828Simp{ 96156828Simp int ret; 97155324Simp 98155324Simp if (rwlock == NULL) 99155324Simp ret = EINVAL; 100155324Simp else { 101155324Simp pthread_rwlock_t prwlock; 102155324Simp 103155324Simp prwlock = *rwlock; 104155324Simp 105155324Simp _pthread_mutex_destroy(&prwlock->lock); 106155324Simp _pthread_cond_destroy(&prwlock->read_signal); 107155324Simp _pthread_cond_destroy(&prwlock->write_signal); 108155324Simp free(prwlock); 109155324Simp 110155324Simp *rwlock = NULL; 111155324Simp 112155324Simp ret = 0; 113155324Simp } 114156828Simp return (ret); 115155324Simp} 116155324Simp 117155324Simpint 118155324Simp_pthread_rwlock_init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) 119155324Simp{ 120155324Simp pthread_rwlock_t prwlock; 121155324Simp int ret; 122155324Simp 123155324Simp /* allocate rwlock object */ 124155324Simp prwlock = (pthread_rwlock_t)malloc(sizeof(struct pthread_rwlock)); 125155324Simp 126155324Simp if (prwlock == NULL) 127155324Simp return (ENOMEM); 128155324Simp 129158746Scognet /* initialize the lock */ 130155324Simp if ((ret = _pthread_mutex_init(&prwlock->lock, NULL)) != 0) 131155324Simp free(prwlock); 132155324Simp else { 133155324Simp /* initialize the read condition signal */ 134155324Simp ret = _pthread_cond_init(&prwlock->read_signal, NULL); 135155324Simp 136155324Simp if (ret != 0) { 137155324Simp _pthread_mutex_destroy(&prwlock->lock); 138155324Simp free(prwlock); 139155324Simp } else { 140155324Simp /* initialize the write condition signal */ 141155324Simp ret = _pthread_cond_init(&prwlock->write_signal, NULL); 142155324Simp 143155324Simp if (ret != 0) { 144155324Simp _pthread_cond_destroy(&prwlock->read_signal); 145155324Simp _pthread_mutex_destroy(&prwlock->lock); 146155324Simp free(prwlock); 147155324Simp } else { 148155324Simp /* success */ 149155324Simp prwlock->state = 0; 150155324Simp prwlock->blocked_writers = 0; 151155324Simp 152155324Simp *rwlock = prwlock; 153155324Simp } 154155324Simp } 155155324Simp } 156155324Simp 157155324Simp return (ret); 158155324Simp} 159155324Simp 160155324Simpstatic int 161155324Simprwlock_rdlock_common (pthread_rwlock_t *rwlock, const struct timespec *abstime) 162155324Simp{ 163155324Simp pthread_rwlock_t prwlock; 164155324Simp struct pthread *curthread; 165155324Simp int ret; 166155324Simp 167155324Simp if (rwlock == NULL) 168155324Simp return (EINVAL); 169155324Simp 170155324Simp prwlock = *rwlock; 171155324Simp 172155324Simp /* check for static initialization */ 173155324Simp if (prwlock == NULL) { 174155324Simp if ((ret = init_static(rwlock)) != 0) 175155324Simp return (ret); 176155324Simp 177155324Simp prwlock = *rwlock; 178155324Simp } 179155324Simp 180155324Simp /* grab the monitor lock */ 181155324Simp if ((ret = _thr_mutex_lock(&prwlock->lock)) != 0) 182155324Simp return (ret); 183155324Simp 184155324Simp /* check lock count */ 185155324Simp if (prwlock->state == MAX_READ_LOCKS) { 186155324Simp _thr_mutex_unlock(&prwlock->lock); 187155324Simp return (EAGAIN); 188155324Simp } 189155324Simp 190155324Simp curthread = _get_curthread(); 191155324Simp if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) { 192155324Simp /* 193155324Simp * To avoid having to track all the rdlocks held by 194155324Simp * a thread or all of the threads that hold a rdlock, 195155324Simp * we keep a simple count of all the rdlocks held by 196155324Simp * a thread. If a thread holds any rdlocks it is 197155324Simp * possible that it is attempting to take a recursive 198155324Simp * rdlock. If there are blocked writers and precedence 199155324Simp * is given to them, then that would result in the thread 200155324Simp * deadlocking. So allowing a thread to take the rdlock 201155324Simp * when it already has one or more rdlocks avoids the 202156828Simp * deadlock. I hope the reader can follow that logic ;-) 203155324Simp */ 204155324Simp ; /* nothing needed */ 205155324Simp } else { 206155324Simp /* give writers priority over readers */ 207155324Simp while (prwlock->blocked_writers || prwlock->state < 0) { 208157564Simp if (abstime) 209157564Simp ret = _pthread_cond_timedwait 210157564Simp (&prwlock->read_signal, 211157564Simp &prwlock->lock, abstime); 212155324Simp else 213155324Simp ret = _thr_cond_wait(&prwlock->read_signal, 214155324Simp &prwlock->lock); 215157564Simp if (ret != 0) { 216155324Simp /* can't do a whole lot if this fails */ 217155324Simp _thr_mutex_unlock(&prwlock->lock); 218155324Simp return (ret); 219155324Simp } 220156828Simp } 221156828Simp } 222156828Simp 223156828Simp curthread->rdlock_count++; 224156828Simp prwlock->state++; /* indicate we are locked for reading */ 225156828Simp 226155324Simp /* 227155324Simp * Something is really wrong if this call fails. Returning 228155324Simp * error won't do because we've already obtained the read 229155324Simp * lock. Decrementing 'state' is no good because we probably 230156828Simp * don't have the monitor lock. 231156828Simp */ 232156828Simp _thr_mutex_unlock(&prwlock->lock); 233156828Simp 234156828Simp return (ret); 235156828Simp} 236156828Simp 237156828Simpint 238156828Simp_pthread_rwlock_rdlock (pthread_rwlock_t *rwlock) 239156828Simp{ 240155324Simp return (rwlock_rdlock_common(rwlock, NULL)); 241156828Simp} 242156828Simp 243156828Simp__strong_reference(_pthread_rwlock_rdlock, _thr_rwlock_rdlock); 244156828Simp 245156828Simpint 246156828Simp_pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock, 247156828Simp const struct timespec *abstime) 248156828Simp{ 249156828Simp return (rwlock_rdlock_common(rwlock, abstime)); 250156828Simp} 251156828Simp 252156828Simpint 253156828Simp_pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock) 254156828Simp{ 255156828Simp struct pthread *curthread; 256156828Simp pthread_rwlock_t prwlock; 257156828Simp int ret; 258156828Simp 259156828Simp if (rwlock == NULL) 260156828Simp return (EINVAL); 261156828Simp 262156828Simp prwlock = *rwlock; 263156828Simp 264156828Simp /* check for static initialization */ 265156828Simp if (prwlock == NULL) { 266156828Simp if ((ret = init_static(rwlock)) != 0) 267156828Simp return (ret); 268156828Simp 269156828Simp prwlock = *rwlock; 270156828Simp } 271156828Simp 272156828Simp /* grab the monitor lock */ 273156828Simp if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0) 274156828Simp return (ret); 275156828Simp 276156828Simp curthread = _get_curthread(); 277156828Simp if (prwlock->state == MAX_READ_LOCKS) 278156828Simp ret = EAGAIN; 279156828Simp else if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) { 280156828Simp /* see comment for pthread_rwlock_rdlock() */ 281156828Simp curthread->rdlock_count++; 282156828Simp prwlock->state++; 283156828Simp } 284156828Simp /* give writers priority over readers */ 285156828Simp else if (prwlock->blocked_writers || prwlock->state < 0) 286156828Simp ret = EBUSY; 287156828Simp else { 288156828Simp curthread->rdlock_count++; 289156828Simp prwlock->state++; /* indicate we are locked for reading */ 290156828Simp } 291156828Simp 292156828Simp /* see the comment on this in pthread_rwlock_rdlock */ 293156828Simp _pthread_mutex_unlock(&prwlock->lock); 294156828Simp 295156828Simp return (ret); 296156828Simp} 297156828Simp 298156828Simpint 299156828Simp_pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock) 300156828Simp{ 301156828Simp pthread_rwlock_t prwlock; 302156828Simp int ret; 303156828Simp 304156828Simp if (rwlock == NULL) 305156828Simp return (EINVAL); 306156828Simp 307156828Simp prwlock = *rwlock; 308156828Simp 309156828Simp /* check for static initialization */ 310156828Simp if (prwlock == NULL) { 311156828Simp if ((ret = init_static(rwlock)) != 0) 312156828Simp return (ret); 313156828Simp 314156828Simp prwlock = *rwlock; 315156828Simp } 316156828Simp 317156828Simp /* grab the monitor lock */ 318156828Simp if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0) 319156828Simp return (ret); 320156828Simp 321158531Scognet if (prwlock->state != 0) 322156828Simp ret = EBUSY; 323156828Simp else 324156828Simp /* indicate we are locked for writing */ 325156828Simp prwlock->state = -1; 326156828Simp 327156828Simp /* see the comment on this in pthread_rwlock_rdlock */ 328156828Simp _pthread_mutex_unlock(&prwlock->lock); 329156828Simp 330156828Simp return (ret); 331156828Simp} 332156828Simp 333156828Simpint 334156828Simp_pthread_rwlock_unlock (pthread_rwlock_t *rwlock) 335156828Simp{ 336156828Simp struct pthread *curthread; 337156828Simp pthread_rwlock_t prwlock; 338156828Simp int ret; 339156828Simp 340156828Simp if (rwlock == NULL) 341156828Simp return (EINVAL); 342156828Simp 343156828Simp prwlock = *rwlock; 344156828Simp 345156828Simp if (prwlock == NULL) 346156828Simp return (EINVAL); 347158531Scognet 348156828Simp /* grab the monitor lock */ 349158531Scognet if ((ret = _thr_mutex_lock(&prwlock->lock)) != 0) 350158531Scognet return (ret); 351158531Scognet 352158531Scognet curthread = _get_curthread(); 353158531Scognet if (prwlock->state > 0) { 354158531Scognet curthread->rdlock_count--; 355156828Simp prwlock->state--; 356156828Simp if (prwlock->state == 0 && prwlock->blocked_writers) 357156828Simp ret = _thr_cond_signal(&prwlock->write_signal); 358156828Simp } else if (prwlock->state < 0) { 359156828Simp prwlock->state = 0; 360156828Simp 361156828Simp if (prwlock->blocked_writers) 362156828Simp ret = _thr_cond_signal(&prwlock->write_signal); 363156828Simp else 364156828Simp ret = _thr_cond_broadcast(&prwlock->read_signal); 365156828Simp } else 366156828Simp ret = EINVAL; 367156828Simp 368156828Simp /* see the comment on this in pthread_rwlock_rdlock */ 369156828Simp _thr_mutex_unlock(&prwlock->lock); 370156828Simp 371156828Simp return (ret); 372156828Simp} 373156828Simp 374156828Simp__strong_reference(_pthread_rwlock_unlock, _thr_rwlock_unlock); 375156828Simp 376156828Simpstatic int 377156828Simprwlock_wrlock_common (pthread_rwlock_t *rwlock, const struct timespec *abstime) 378156828Simp{ 379156828Simp pthread_rwlock_t prwlock; 380156828Simp int ret; 381156828Simp 382156828Simp if (rwlock == NULL) 383156828Simp return (EINVAL); 384156828Simp 385156828Simp prwlock = *rwlock; 386156828Simp 387156828Simp /* check for static initialization */ 388156828Simp if (prwlock == NULL) { 389156828Simp if ((ret = init_static(rwlock)) != 0) 390156828Simp return (ret); 391156828Simp 392156828Simp prwlock = *rwlock; 393156828Simp } 394156828Simp 395156828Simp /* grab the monitor lock */ 396156828Simp if ((ret = _thr_mutex_lock(&prwlock->lock)) != 0) 397156828Simp return (ret); 398156828Simp 399155324Simp while (prwlock->state != 0) { 400155324Simp prwlock->blocked_writers++; 401155324Simp 402155324Simp if (abstime != NULL) 403155324Simp ret = _pthread_cond_timedwait(&prwlock->write_signal, 404155324Simp &prwlock->lock, abstime); 405155324Simp else 406155324Simp ret = _thr_cond_wait(&prwlock->write_signal, 407155324Simp &prwlock->lock); 408155324Simp if (ret != 0) { 409155324Simp prwlock->blocked_writers--; 410155324Simp _thr_mutex_unlock(&prwlock->lock); 411155324Simp return (ret); 412155324Simp } 413155324Simp 414155324Simp prwlock->blocked_writers--; 415155324Simp } 416156828Simp 417156828Simp /* indicate we are locked for writing */ 418156828Simp prwlock->state = -1; 419156828Simp 420155324Simp /* see the comment on this in pthread_rwlock_rdlock */ 421155324Simp _thr_mutex_unlock(&prwlock->lock); 422155324Simp 423155324Simp return (ret); 424161704Scognet} 425161704Scognet 426155324Simpint 427156828Simp_pthread_rwlock_wrlock (pthread_rwlock_t *rwlock) 428156828Simp{ 429156828Simp return (rwlock_wrlock_common (rwlock, NULL)); 430155324Simp} 431155324Simp__strong_reference(_pthread_rwlock_wrlock, _thr_rwlock_wrlock); 432155324Simp 433155324Simpint 434155324Simp_pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock, 435155324Simp const struct timespec *abstime) 436155324Simp{ 437155324Simp return (rwlock_wrlock_common (rwlock, abstime)); 438155324Simp} 439155324Simp