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