thr_rwlock.c revision 50476
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/libkse/thread/thr_rwlock.c 50476 1999-08-28 00:22:10Z peter $ 27 */ 28 29#ifdef _THREAD_SAFE 30#include <errno.h> 31#include <limits.h> 32#include <stdlib.h> 33 34#include <pthread.h> 35#include "pthread_private.h" 36 37/* maximum number of times a read lock may be obtained */ 38#define MAX_READ_LOCKS (INT_MAX - 1) 39 40static int init_static (pthread_rwlock_t *rwlock); 41 42static spinlock_t static_init_lock = _SPINLOCK_INITIALIZER; 43 44static int 45init_static (pthread_rwlock_t *rwlock) 46{ 47 int ret; 48 49 _SPINLOCK(&static_init_lock); 50 51 if (*rwlock == NULL) 52 ret = pthread_rwlock_init(rwlock, NULL); 53 else 54 ret = 0; 55 56 _SPINUNLOCK(&static_init_lock); 57 58 return(ret); 59} 60 61int 62pthread_rwlock_destroy (pthread_rwlock_t *rwlock) 63{ 64 int ret; 65 66 if (rwlock == NULL) 67 ret = EINVAL; 68 else { 69 pthread_rwlock_t prwlock; 70 71 prwlock = *rwlock; 72 73 pthread_mutex_destroy(&prwlock->lock); 74 pthread_cond_destroy(&prwlock->read_signal); 75 pthread_cond_destroy(&prwlock->write_signal); 76 free(prwlock); 77 78 *rwlock = NULL; 79 80 ret = 0; 81 } 82 83 return(ret); 84} 85 86int 87pthread_rwlock_init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) 88{ 89 pthread_rwlock_t prwlock; 90 int ret; 91 92 /* allocate rwlock object */ 93 prwlock = (pthread_rwlock_t)malloc(sizeof(struct pthread_rwlock)); 94 95 if (prwlock == NULL) 96 return(ENOMEM); 97 98 /* initialize the lock */ 99 if ((ret = pthread_mutex_init(&prwlock->lock, NULL)) != 0) 100 free(prwlock); 101 else { 102 /* initialize the read condition signal */ 103 ret = pthread_cond_init(&prwlock->read_signal, NULL); 104 105 if (ret != 0) { 106 pthread_mutex_destroy(&prwlock->lock); 107 free(prwlock); 108 } else { 109 /* initialize the write condition signal */ 110 ret = pthread_cond_init(&prwlock->write_signal, NULL); 111 112 if (ret != 0) { 113 pthread_cond_destroy(&prwlock->read_signal); 114 pthread_mutex_destroy(&prwlock->lock); 115 free(prwlock); 116 } else { 117 /* success */ 118 prwlock->state = 0; 119 prwlock->blocked_writers = 0; 120 121 *rwlock = prwlock; 122 } 123 } 124 } 125 126 return(ret); 127} 128 129int 130pthread_rwlock_rdlock (pthread_rwlock_t *rwlock) 131{ 132 pthread_rwlock_t prwlock; 133 int ret; 134 135 if (rwlock == NULL) 136 return(EINVAL); 137 138 prwlock = *rwlock; 139 140 /* check for static initialization */ 141 if (prwlock == NULL) { 142 if ((ret = init_static(rwlock)) != 0) 143 return(ret); 144 145 prwlock = *rwlock; 146 } 147 148 /* grab the monitor lock */ 149 if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0) 150 return(ret); 151 152 /* give writers priority over readers */ 153 while (prwlock->blocked_writers || prwlock->state < 0) { 154 ret = pthread_cond_wait(&prwlock->read_signal, &prwlock->lock); 155 156 if (ret != 0) { 157 /* can't do a whole lot if this fails */ 158 pthread_mutex_unlock(&prwlock->lock); 159 return(ret); 160 } 161 } 162 163 /* check lock count */ 164 if (prwlock->state == MAX_READ_LOCKS) 165 ret = EAGAIN; 166 else 167 ++prwlock->state; /* indicate we are locked for reading */ 168 169 /* 170 * Something is really wrong if this call fails. Returning 171 * error won't do because we've already obtained the read 172 * lock. Decrementing 'state' is no good because we probably 173 * don't have the monitor lock. 174 */ 175 pthread_mutex_unlock(&prwlock->lock); 176 177 return(ret); 178} 179 180int 181pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock) 182{ 183 pthread_rwlock_t prwlock; 184 int ret; 185 186 if (rwlock == NULL) 187 return(EINVAL); 188 189 prwlock = *rwlock; 190 191 /* check for static initialization */ 192 if (prwlock == NULL) { 193 if ((ret = init_static(rwlock)) != 0) 194 return(ret); 195 196 prwlock = *rwlock; 197 } 198 199 /* grab the monitor lock */ 200 if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0) 201 return(ret); 202 203 /* give writers priority over readers */ 204 if (prwlock->blocked_writers || prwlock->state < 0) 205 ret = EWOULDBLOCK; 206 else if (prwlock->state == MAX_READ_LOCKS) 207 ret = EAGAIN; /* too many read locks acquired */ 208 else 209 ++prwlock->state; /* indicate we are locked for reading */ 210 211 /* see the comment on this in pthread_rwlock_rdlock */ 212 pthread_mutex_unlock(&prwlock->lock); 213 214 return(ret); 215} 216 217int 218pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock) 219{ 220 pthread_rwlock_t prwlock; 221 int ret; 222 223 if (rwlock == NULL) 224 return(EINVAL); 225 226 prwlock = *rwlock; 227 228 /* check for static initialization */ 229 if (prwlock == NULL) { 230 if ((ret = init_static(rwlock)) != 0) 231 return(ret); 232 233 prwlock = *rwlock; 234 } 235 236 /* grab the monitor lock */ 237 if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0) 238 return(ret); 239 240 if (prwlock->state != 0) 241 ret = EWOULDBLOCK; 242 else 243 /* indicate we are locked for writing */ 244 prwlock->state = -1; 245 246 /* see the comment on this in pthread_rwlock_rdlock */ 247 pthread_mutex_unlock(&prwlock->lock); 248 249 return(ret); 250} 251 252int 253pthread_rwlock_unlock (pthread_rwlock_t *rwlock) 254{ 255 pthread_rwlock_t prwlock; 256 int ret; 257 258 if (rwlock == NULL) 259 return(EINVAL); 260 261 prwlock = *rwlock; 262 263 if (prwlock == NULL) 264 return(EINVAL); 265 266 /* grab the monitor lock */ 267 if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0) 268 return(ret); 269 270 if (prwlock->state > 0) { 271 if (--prwlock->state == 0 && prwlock->blocked_writers) 272 ret = pthread_cond_signal(&prwlock->write_signal); 273 } else if (prwlock->state < 0) { 274 prwlock->state = 0; 275 276 if (prwlock->blocked_writers) 277 ret = pthread_cond_signal(&prwlock->write_signal); 278 else 279 ret = pthread_cond_broadcast(&prwlock->read_signal); 280 } else 281 ret = EINVAL; 282 283 /* see the comment on this in pthread_rwlock_rdlock */ 284 pthread_mutex_unlock(&prwlock->lock); 285 286 return(ret); 287} 288 289int 290pthread_rwlock_wrlock (pthread_rwlock_t *rwlock) 291{ 292 pthread_rwlock_t prwlock; 293 int ret; 294 295 if (rwlock == NULL) 296 return(EINVAL); 297 298 prwlock = *rwlock; 299 300 /* check for static initialization */ 301 if (prwlock == NULL) { 302 if ((ret = init_static(rwlock)) != 0) 303 return(ret); 304 305 prwlock = *rwlock; 306 } 307 308 /* grab the monitor lock */ 309 if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0) 310 return(ret); 311 312 while (prwlock->state != 0) { 313 ++prwlock->blocked_writers; 314 315 ret = pthread_cond_wait(&prwlock->write_signal, &prwlock->lock); 316 317 if (ret != 0) { 318 --prwlock->blocked_writers; 319 pthread_mutex_unlock(&prwlock->lock); 320 return(ret); 321 } 322 323 --prwlock->blocked_writers; 324 } 325 326 /* indicate we are locked for writing */ 327 prwlock->state = -1; 328 329 /* see the comment on this in pthread_rwlock_rdlock */ 330 pthread_mutex_unlock(&prwlock->lock); 331 332 return(ret); 333} 334 335#endif /* _THREAD_SAFE */ 336