thr_rwlock.c revision 124586
1/*- 2 * Copyright (c) 1998 Alex Nash 3 * Copyright (c) 2004 Michael Telahun Makonnen 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD: head/lib/libthr/thread/thr_rwlock.c 124586 2004-01-16 10:52:10Z mtm $ 28 */ 29 30#include <errno.h> 31#include <limits.h> 32#include <stdlib.h> 33 34#include <pthread.h> 35#include "thr_private.h" 36 37/* maximum number of times a read lock may be obtained */ 38#define MAX_READ_LOCKS (INT_MAX - 1) 39 40__weak_reference(_pthread_rwlock_destroy, pthread_rwlock_destroy); 41__weak_reference(_pthread_rwlock_init, pthread_rwlock_init); 42__weak_reference(_pthread_rwlock_rdlock, pthread_rwlock_rdlock); 43__weak_reference(_pthread_rwlock_timedrdlock, pthread_rwlock_timedrdlock); 44__weak_reference(_pthread_rwlock_timedwrlock, pthread_rwlock_timedwrlock); 45__weak_reference(_pthread_rwlock_tryrdlock, pthread_rwlock_tryrdlock); 46__weak_reference(_pthread_rwlock_trywrlock, pthread_rwlock_trywrlock); 47__weak_reference(_pthread_rwlock_unlock, pthread_rwlock_unlock); 48__weak_reference(_pthread_rwlock_wrlock, pthread_rwlock_wrlock); 49 50static int rwlock_rdlock_common(pthread_rwlock_t *, int, 51 const struct timespec *); 52static int rwlock_wrlock_common(pthread_rwlock_t *, int, 53 const struct timespec *); 54 55int 56_pthread_rwlock_destroy (pthread_rwlock_t *rwlock) 57{ 58 pthread_rwlock_t prwlock; 59 60 if (rwlock == NULL || *rwlock == NULL) 61 return (EINVAL); 62 63 prwlock = *rwlock; 64 65 if (prwlock->state != 0) 66 return (EBUSY); 67 68 pthread_mutex_destroy(&prwlock->lock); 69 pthread_cond_destroy(&prwlock->read_signal); 70 pthread_cond_destroy(&prwlock->write_signal); 71 free(prwlock); 72 73 *rwlock = NULL; 74 75 return (0); 76} 77 78int 79_pthread_rwlock_init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) 80{ 81 pthread_rwlock_t prwlock; 82 int ret; 83 84 /* allocate rwlock object */ 85 prwlock = (pthread_rwlock_t)malloc(sizeof(struct pthread_rwlock)); 86 87 if (prwlock == NULL) 88 return(ENOMEM); 89 90 /* initialize the lock */ 91 if ((ret = pthread_mutex_init(&prwlock->lock, NULL)) != 0) 92 goto out_mutex; 93 94 /* initialize the read condition signal */ 95 if ((ret = pthread_cond_init(&prwlock->read_signal, NULL)) != 0) 96 goto out_readcond; 97 98 /* initialize the write condition signal */ 99 if ((ret = pthread_cond_init(&prwlock->write_signal, NULL)) != 0) 100 goto out_writecond; 101 102 /* success */ 103 prwlock->state = 0; 104 prwlock->blocked_writers = 0; 105 106 *rwlock = prwlock; 107 return (0); 108 109out_writecond: 110 pthread_cond_destroy(&prwlock->read_signal); 111out_readcond: 112 pthread_mutex_destroy(&prwlock->lock); 113out_mutex: 114 free(prwlock); 115 return(ret); 116} 117 118/* 119 * If nonblocking is 0 this function will wait on the lock. If 120 * it is greater than 0 it will return immediately with EBUSY. 121 */ 122static int 123rwlock_rdlock_common(pthread_rwlock_t *rwlock, int nonblocking, 124 const struct timespec *timeout) 125{ 126 pthread_rwlock_t prwlock; 127 int ret; 128 129 if (rwlock == NULL || *rwlock == NULL) 130 return(EINVAL); 131 132 /* 133 * Check for validity of the timeout parameter. 134 */ 135 if (timeout != NULL && 136 (timeout->tv_nsec < 0 || timeout->tv_nsec >= 1000000000)) 137 return (EINVAL); 138 139 prwlock = *rwlock; 140 141 /* grab the monitor lock */ 142 if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0) 143 return(ret); 144 145 /* check lock count */ 146 if (prwlock->state == MAX_READ_LOCKS) { 147 pthread_mutex_unlock(&prwlock->lock); 148 return (EAGAIN); 149 } 150 151 /* give writers priority over readers */ 152 while (prwlock->blocked_writers || prwlock->state < 0) { 153 if (nonblocking) { 154 pthread_mutex_unlock(&prwlock->lock); 155 return (EBUSY); 156 } 157 158 if (timeout == NULL) 159 ret = pthread_cond_wait(&prwlock->read_signal, 160 &prwlock->lock); 161 else 162 ret = pthread_cond_timedwait(&prwlock->read_signal, 163 &prwlock->lock, timeout); 164 165 if (ret != 0 && ret != EINTR) { 166 /* can't do a whole lot if this fails */ 167 pthread_mutex_unlock(&prwlock->lock); 168 return(ret); 169 } 170 } 171 172 ++prwlock->state; /* indicate we are locked for reading */ 173 174 /* 175 * Something is really wrong if this call fails. Returning 176 * error won't do because we've already obtained the read 177 * lock. Decrementing 'state' is no good because we probably 178 * don't have the monitor lock. 179 */ 180 pthread_mutex_unlock(&prwlock->lock); 181 182 return(0); 183} 184 185int 186_pthread_rwlock_rdlock (pthread_rwlock_t *rwlock) 187{ 188 return (rwlock_rdlock_common(rwlock, 0, NULL)); 189} 190 191int 192_pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock, 193 const struct timespec *timeout) 194{ 195 return (rwlock_rdlock_common(rwlock, 0, timeout)); 196} 197 198int 199_pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock) 200{ 201 return (rwlock_rdlock_common(rwlock, 1, NULL)); 202} 203 204int 205_pthread_rwlock_unlock (pthread_rwlock_t *rwlock) 206{ 207 pthread_rwlock_t prwlock; 208 int ret; 209 210 if (rwlock == NULL || *rwlock == NULL) 211 return(EINVAL); 212 213 prwlock = *rwlock; 214 215 /* grab the monitor lock */ 216 if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0) 217 return(ret); 218 219 /* XXX - Make sure we hold a lock on this rwlock */ 220 221 if (prwlock->state > 0) { 222 if (--prwlock->state == 0 && prwlock->blocked_writers) 223 ret = pthread_cond_signal(&prwlock->write_signal); 224 } else if (prwlock->state < 0) { 225 prwlock->state = 0; 226 227 if (prwlock->blocked_writers) 228 ret = pthread_cond_signal(&prwlock->write_signal); 229 else 230 ret = pthread_cond_broadcast(&prwlock->read_signal); 231 } 232 233 /* see the comment on this in rwlock_rdlock_common */ 234 pthread_mutex_unlock(&prwlock->lock); 235 236 return(ret); 237} 238 239int 240_pthread_rwlock_wrlock (pthread_rwlock_t *rwlock) 241{ 242 return (rwlock_wrlock_common(rwlock, 0, NULL)); 243} 244 245int 246_pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock, 247 const struct timespec *timeout) 248{ 249 return (rwlock_wrlock_common(rwlock, 0, timeout)); 250} 251 252int 253_pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock) 254{ 255 return (rwlock_wrlock_common(rwlock, 1, NULL)); 256} 257 258/* 259 * If nonblocking is 0 this function will wait on the lock. If 260 * it is greater than 0 it will return immediately with EBUSY. 261 */ 262static int 263rwlock_wrlock_common(pthread_rwlock_t *rwlock, int nonblocking, 264 const struct timespec *timeout) 265{ 266 pthread_rwlock_t prwlock; 267 int ret; 268 269 if (rwlock == NULL || *rwlock == NULL) 270 return(EINVAL); 271 272 /* 273 * Check the timeout value for validity. 274 */ 275 if (timeout != NULL && 276 (timeout->tv_nsec < 0 || timeout->tv_nsec >= 1000000000)) 277 return (EINVAL); 278 279 prwlock = *rwlock; 280 281 /* grab the monitor lock */ 282 if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0) 283 return(ret); 284 285 while (prwlock->state != 0) { 286 if (nonblocking) { 287 pthread_mutex_unlock(&prwlock->lock); 288 return (EBUSY); 289 } 290 291 ++prwlock->blocked_writers; 292 293 if (timeout == NULL) 294 ret = pthread_cond_wait(&prwlock->write_signal, 295 &prwlock->lock); 296 else 297 ret = pthread_cond_timedwait(&prwlock->write_signal, 298 &prwlock->lock, timeout); 299 300 if (ret != 0 && ret != EINTR) { 301 --prwlock->blocked_writers; 302 pthread_mutex_unlock(&prwlock->lock); 303 return(ret); 304 } 305 306 --prwlock->blocked_writers; 307 } 308 309 /* indicate we are locked for writing */ 310 prwlock->state = -1; 311 312 /* see the comment on this in pthread_rwlock_rdlock */ 313 pthread_mutex_unlock(&prwlock->lock); 314 315 return(0); 316} 317 318