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