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