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