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$ 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 47 static int init_static (pthread_rwlock_t *rwlock); 48 49 static spinlock_t static_init_lock = _SPINLOCK_INITIALIZER; 50 51 static int 52 init_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 68 int 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 93 int 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 136 int 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 187 int 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 224 int 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 259 int 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 296 int 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