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: src/lib/libpthread/thread/thr_rwlock.c,v 1.14 2004/01/08 15:37:09 deischen Exp $ 27 */ 28 29 #include "namespace.h" 30 #include <machine/tls.h> 31 #include <errno.h> 32 #include <limits.h> 33 #include <stdlib.h> 34 #include <pthread.h> 35 #include "un-namespace.h" 36 #include "thr_private.h" 37 38 /* maximum number of times a read lock may be obtained */ 39 #define MAX_READ_LOCKS (INT_MAX - 1) 40 41 umtx_t _rwlock_static_lock; 42 43 static int 44 rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr __unused) 45 { 46 pthread_rwlock_t prwlock; 47 int ret; 48 49 /* allocate rwlock object */ 50 prwlock = (pthread_rwlock_t)malloc(sizeof(struct pthread_rwlock)); 51 52 if (prwlock == NULL) 53 return (ENOMEM); 54 55 /* initialize the lock */ 56 if ((ret = _pthread_mutex_init(&prwlock->lock, NULL)) != 0) 57 free(prwlock); 58 else { 59 /* initialize the read condition signal */ 60 ret = _pthread_cond_init(&prwlock->read_signal, NULL); 61 62 if (ret != 0) { 63 _pthread_mutex_destroy(&prwlock->lock); 64 free(prwlock); 65 } else { 66 /* initialize the write condition signal */ 67 ret = _pthread_cond_init(&prwlock->write_signal, NULL); 68 69 if (ret != 0) { 70 _pthread_cond_destroy(&prwlock->read_signal); 71 _pthread_mutex_destroy(&prwlock->lock); 72 free(prwlock); 73 } else { 74 /* success */ 75 prwlock->state = 0; 76 prwlock->blocked_writers = 0; 77 *rwlock = prwlock; 78 } 79 } 80 } 81 82 return (ret); 83 } 84 85 int 86 _pthread_rwlock_destroy (pthread_rwlock_t *rwlock) 87 { 88 int ret; 89 90 if (rwlock == NULL) 91 ret = EINVAL; 92 else if (*rwlock == NULL) 93 ret = 0; 94 else { 95 pthread_rwlock_t prwlock; 96 97 prwlock = *rwlock; 98 99 _pthread_mutex_destroy(&prwlock->lock); 100 _pthread_cond_destroy(&prwlock->read_signal); 101 _pthread_cond_destroy(&prwlock->write_signal); 102 free(prwlock); 103 104 *rwlock = NULL; 105 106 ret = 0; 107 } 108 return (ret); 109 } 110 111 static int 112 init_static(struct pthread *thread, pthread_rwlock_t *rwlock) 113 { 114 int ret; 115 116 THR_LOCK_ACQUIRE(thread, &_rwlock_static_lock); 117 118 if (*rwlock == NULL) 119 ret = rwlock_init(rwlock, NULL); 120 else 121 ret = 0; 122 123 THR_LOCK_RELEASE(thread, &_rwlock_static_lock); 124 125 return (ret); 126 } 127 128 int 129 _pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) 130 { 131 *rwlock = NULL; 132 return (rwlock_init(rwlock, attr)); 133 } 134 135 static int 136 rwlock_rdlock_common(pthread_rwlock_t *rwlock, const struct timespec *abstime) 137 { 138 struct pthread *curthread = tls_get_curthread(); 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(curthread, 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 /* check lock count */ 160 if (prwlock->state == MAX_READ_LOCKS) { 161 _pthread_mutex_unlock(&prwlock->lock); 162 return (EAGAIN); 163 } 164 165 curthread = tls_get_curthread(); 166 if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) { 167 /* 168 * To avoid having to track all the rdlocks held by 169 * a thread or all of the threads that hold a rdlock, 170 * we keep a simple count of all the rdlocks held by 171 * a thread. If a thread holds any rdlocks it is 172 * possible that it is attempting to take a recursive 173 * rdlock. If there are blocked writers and precedence 174 * is given to them, then that would result in the thread 175 * deadlocking. So allowing a thread to take the rdlock 176 * when it already has one or more rdlocks avoids the 177 * deadlock. I hope the reader can follow that logic ;-) 178 */ 179 ; /* nothing needed */ 180 } else { 181 /* give writers priority over readers */ 182 while (prwlock->blocked_writers || prwlock->state < 0) { 183 if (abstime) 184 ret = _pthread_cond_timedwait 185 (&prwlock->read_signal, 186 &prwlock->lock, abstime); 187 else 188 ret = _pthread_cond_wait(&prwlock->read_signal, 189 &prwlock->lock); 190 if (ret != 0) { 191 /* can't do a whole lot if this fails */ 192 _pthread_mutex_unlock(&prwlock->lock); 193 return (ret); 194 } 195 } 196 } 197 198 curthread->rdlock_count++; 199 prwlock->state++; /* indicate we are locked for reading */ 200 201 /* 202 * Something is really wrong if this call fails. Returning 203 * error won't do because we've already obtained the read 204 * lock. Decrementing 'state' is no good because we probably 205 * don't have the monitor lock. 206 */ 207 _pthread_mutex_unlock(&prwlock->lock); 208 209 return (ret); 210 } 211 212 int 213 _pthread_rwlock_rdlock (pthread_rwlock_t *rwlock) 214 { 215 return (rwlock_rdlock_common(rwlock, NULL)); 216 } 217 218 int 219 _pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock, 220 const struct timespec *abstime) 221 { 222 return (rwlock_rdlock_common(rwlock, abstime)); 223 } 224 225 int 226 _pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock) 227 { 228 struct pthread *curthread = tls_get_curthread(); 229 pthread_rwlock_t prwlock; 230 int ret; 231 232 if (rwlock == NULL) 233 return (EINVAL); 234 235 prwlock = *rwlock; 236 237 /* check for static initialization */ 238 if (prwlock == NULL) { 239 if ((ret = init_static(curthread, rwlock)) != 0) 240 return (ret); 241 242 prwlock = *rwlock; 243 } 244 245 /* grab the monitor lock */ 246 if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0) 247 return (ret); 248 249 curthread = tls_get_curthread(); 250 if (prwlock->state == MAX_READ_LOCKS) 251 ret = EAGAIN; 252 else if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) { 253 /* see comment for pthread_rwlock_rdlock() */ 254 curthread->rdlock_count++; 255 prwlock->state++; 256 } 257 /* give writers priority over readers */ 258 else if (prwlock->blocked_writers || prwlock->state < 0) 259 ret = EBUSY; 260 else { 261 curthread->rdlock_count++; 262 prwlock->state++; /* indicate we are locked for reading */ 263 } 264 265 /* see the comment on this in pthread_rwlock_rdlock */ 266 _pthread_mutex_unlock(&prwlock->lock); 267 268 return (ret); 269 } 270 271 int 272 _pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock) 273 { 274 struct pthread *curthread = tls_get_curthread(); 275 pthread_rwlock_t prwlock; 276 int ret; 277 278 if (rwlock == NULL) 279 return (EINVAL); 280 281 prwlock = *rwlock; 282 283 /* check for static initialization */ 284 if (prwlock == NULL) { 285 if ((ret = init_static(curthread, rwlock)) != 0) 286 return (ret); 287 288 prwlock = *rwlock; 289 } 290 291 /* grab the monitor lock */ 292 if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0) 293 return (ret); 294 295 if (prwlock->state != 0) 296 ret = EBUSY; 297 else 298 /* indicate we are locked for writing */ 299 prwlock->state = -1; 300 301 /* see the comment on this in pthread_rwlock_rdlock */ 302 _pthread_mutex_unlock(&prwlock->lock); 303 304 return (ret); 305 } 306 307 int 308 _pthread_rwlock_unlock (pthread_rwlock_t *rwlock) 309 { 310 struct pthread *curthread; 311 pthread_rwlock_t prwlock; 312 int ret; 313 314 if (rwlock == NULL) 315 return (EINVAL); 316 317 prwlock = *rwlock; 318 319 if (prwlock == NULL) 320 return (EINVAL); 321 322 /* grab the monitor lock */ 323 if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0) 324 return (ret); 325 326 curthread = tls_get_curthread(); 327 if (prwlock->state > 0) { 328 curthread->rdlock_count--; 329 prwlock->state--; 330 if (prwlock->state == 0 && prwlock->blocked_writers) 331 ret = _pthread_cond_signal(&prwlock->write_signal); 332 } else if (prwlock->state < 0) { 333 prwlock->state = 0; 334 335 if (prwlock->blocked_writers) 336 ret = _pthread_cond_signal(&prwlock->write_signal); 337 else 338 ret = _pthread_cond_broadcast(&prwlock->read_signal); 339 } else 340 ret = EINVAL; 341 342 /* see the comment on this in pthread_rwlock_rdlock */ 343 _pthread_mutex_unlock(&prwlock->lock); 344 345 return (ret); 346 } 347 348 static int 349 rwlock_wrlock_common (pthread_rwlock_t *rwlock, const struct timespec *abstime) 350 { 351 struct pthread *curthread = tls_get_curthread(); 352 pthread_rwlock_t prwlock; 353 int ret; 354 355 if (rwlock == NULL) 356 return (EINVAL); 357 358 prwlock = *rwlock; 359 360 /* check for static initialization */ 361 if (prwlock == NULL) { 362 if ((ret = init_static(curthread, rwlock)) != 0) 363 return (ret); 364 365 prwlock = *rwlock; 366 } 367 368 /* grab the monitor lock */ 369 if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0) 370 return (ret); 371 372 while (prwlock->state != 0) { 373 prwlock->blocked_writers++; 374 375 if (abstime != NULL) 376 ret = _pthread_cond_timedwait(&prwlock->write_signal, 377 &prwlock->lock, abstime); 378 else 379 ret = _pthread_cond_wait(&prwlock->write_signal, 380 &prwlock->lock); 381 if (ret != 0) { 382 prwlock->blocked_writers--; 383 _pthread_mutex_unlock(&prwlock->lock); 384 return (ret); 385 } 386 387 prwlock->blocked_writers--; 388 } 389 390 /* indicate we are locked for writing */ 391 prwlock->state = -1; 392 393 /* see the comment on this in pthread_rwlock_rdlock */ 394 _pthread_mutex_unlock(&prwlock->lock); 395 396 return (ret); 397 } 398 399 int 400 _pthread_rwlock_wrlock (pthread_rwlock_t *rwlock) 401 { 402 return (rwlock_wrlock_common (rwlock, NULL)); 403 } 404 405 int 406 _pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock, 407 const struct timespec *abstime) 408 { 409 return (rwlock_wrlock_common (rwlock, abstime)); 410 } 411 412 __strong_reference(_pthread_rwlock_destroy, pthread_rwlock_destroy); 413 __strong_reference(_pthread_rwlock_init, pthread_rwlock_init); 414 __strong_reference(_pthread_rwlock_rdlock, pthread_rwlock_rdlock); 415 __strong_reference(_pthread_rwlock_timedrdlock, pthread_rwlock_timedrdlock); 416 __strong_reference(_pthread_rwlock_tryrdlock, pthread_rwlock_tryrdlock); 417 __strong_reference(_pthread_rwlock_trywrlock, pthread_rwlock_trywrlock); 418 __strong_reference(_pthread_rwlock_unlock, pthread_rwlock_unlock); 419 __strong_reference(_pthread_rwlock_wrlock, pthread_rwlock_wrlock); 420 __strong_reference(_pthread_rwlock_timedwrlock, pthread_rwlock_timedwrlock); 421