1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/nsprpub/pr/src/threads/prrwlock.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,483 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "primpl.h" 1.10 + 1.11 +#include <string.h> 1.12 + 1.13 +#if defined(HPUX) && defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) 1.14 + 1.15 +#include <pthread.h> 1.16 +#define HAVE_UNIX98_RWLOCK 1.17 +#define RWLOCK_T pthread_rwlock_t 1.18 +#define RWLOCK_INIT(lock) pthread_rwlock_init(lock, NULL) 1.19 +#define RWLOCK_DESTROY(lock) pthread_rwlock_destroy(lock) 1.20 +#define RWLOCK_RDLOCK(lock) pthread_rwlock_rdlock(lock) 1.21 +#define RWLOCK_WRLOCK(lock) pthread_rwlock_wrlock(lock) 1.22 +#define RWLOCK_UNLOCK(lock) pthread_rwlock_unlock(lock) 1.23 + 1.24 +#elif defined(SOLARIS) && (defined(_PR_PTHREADS) \ 1.25 + || defined(_PR_GLOBAL_THREADS_ONLY)) 1.26 + 1.27 +#include <synch.h> 1.28 +#define HAVE_UI_RWLOCK 1.29 +#define RWLOCK_T rwlock_t 1.30 +#define RWLOCK_INIT(lock) rwlock_init(lock, USYNC_THREAD, NULL) 1.31 +#define RWLOCK_DESTROY(lock) rwlock_destroy(lock) 1.32 +#define RWLOCK_RDLOCK(lock) rw_rdlock(lock) 1.33 +#define RWLOCK_WRLOCK(lock) rw_wrlock(lock) 1.34 +#define RWLOCK_UNLOCK(lock) rw_unlock(lock) 1.35 + 1.36 +#endif 1.37 + 1.38 +/* 1.39 + * Reader-writer lock 1.40 + */ 1.41 +struct PRRWLock { 1.42 + char *rw_name; /* lock name */ 1.43 + PRUint32 rw_rank; /* rank of the lock */ 1.44 + 1.45 +#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) 1.46 + RWLOCK_T rw_lock; 1.47 +#else 1.48 + PRLock *rw_lock; 1.49 + PRInt32 rw_lock_cnt; /* == 0, if unlocked */ 1.50 + /* == -1, if write-locked */ 1.51 + /* > 0 , # of read locks */ 1.52 + PRUint32 rw_reader_cnt; /* number of waiting readers */ 1.53 + PRUint32 rw_writer_cnt; /* number of waiting writers */ 1.54 + PRCondVar *rw_reader_waitq; /* cvar for readers */ 1.55 + PRCondVar *rw_writer_waitq; /* cvar for writers */ 1.56 +#ifdef DEBUG 1.57 + PRThread *rw_owner; /* lock owner for write-lock */ 1.58 +#endif 1.59 +#endif 1.60 +}; 1.61 + 1.62 +#ifdef DEBUG 1.63 +#define _PR_RWLOCK_RANK_ORDER_DEBUG /* enable deadlock detection using 1.64 + rank-order for locks 1.65 + */ 1.66 +#endif 1.67 + 1.68 +#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG 1.69 + 1.70 +static PRUintn pr_thread_rwlock_key; /* TPD key for lock stack */ 1.71 +static PRUintn pr_thread_rwlock_alloc_failed; 1.72 + 1.73 +#define _PR_RWLOCK_RANK_ORDER_LIMIT 10 1.74 + 1.75 +typedef struct thread_rwlock_stack { 1.76 + PRInt32 trs_index; /* top of stack */ 1.77 + PRRWLock *trs_stack[_PR_RWLOCK_RANK_ORDER_LIMIT]; /* stack of lock 1.78 + pointers */ 1.79 + 1.80 +} thread_rwlock_stack; 1.81 + 1.82 +static void _PR_SET_THREAD_RWLOCK_RANK(PRRWLock *rwlock); 1.83 +static PRUint32 _PR_GET_THREAD_RWLOCK_RANK(void); 1.84 +static void _PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock *rwlock); 1.85 +static void _PR_RELEASE_LOCK_STACK(void *lock_stack); 1.86 + 1.87 +#endif 1.88 + 1.89 +/* 1.90 + * Reader/Writer Locks 1.91 + */ 1.92 + 1.93 +/* 1.94 + * PR_NewRWLock 1.95 + * Create a reader-writer lock, with the given lock rank and lock name 1.96 + * 1.97 + */ 1.98 + 1.99 +PR_IMPLEMENT(PRRWLock *) 1.100 +PR_NewRWLock(PRUint32 lock_rank, const char *lock_name) 1.101 +{ 1.102 + PRRWLock *rwlock; 1.103 +#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) 1.104 + int err; 1.105 +#endif 1.106 + 1.107 + if (!_pr_initialized) _PR_ImplicitInitialization(); 1.108 + 1.109 + rwlock = PR_NEWZAP(PRRWLock); 1.110 + if (rwlock == NULL) 1.111 + return NULL; 1.112 + 1.113 + rwlock->rw_rank = lock_rank; 1.114 + if (lock_name != NULL) { 1.115 + rwlock->rw_name = (char*) PR_Malloc(strlen(lock_name) + 1); 1.116 + if (rwlock->rw_name == NULL) { 1.117 + PR_DELETE(rwlock); 1.118 + return(NULL); 1.119 + } 1.120 + strcpy(rwlock->rw_name, lock_name); 1.121 + } else { 1.122 + rwlock->rw_name = NULL; 1.123 + } 1.124 + 1.125 +#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) 1.126 + err = RWLOCK_INIT(&rwlock->rw_lock); 1.127 + if (err != 0) { 1.128 + PR_SetError(PR_UNKNOWN_ERROR, err); 1.129 + PR_Free(rwlock->rw_name); 1.130 + PR_DELETE(rwlock); 1.131 + return NULL; 1.132 + } 1.133 + return rwlock; 1.134 +#else 1.135 + rwlock->rw_lock = PR_NewLock(); 1.136 + if (rwlock->rw_lock == NULL) { 1.137 + goto failed; 1.138 + } 1.139 + rwlock->rw_reader_waitq = PR_NewCondVar(rwlock->rw_lock); 1.140 + if (rwlock->rw_reader_waitq == NULL) { 1.141 + goto failed; 1.142 + } 1.143 + rwlock->rw_writer_waitq = PR_NewCondVar(rwlock->rw_lock); 1.144 + if (rwlock->rw_writer_waitq == NULL) { 1.145 + goto failed; 1.146 + } 1.147 + rwlock->rw_reader_cnt = 0; 1.148 + rwlock->rw_writer_cnt = 0; 1.149 + rwlock->rw_lock_cnt = 0; 1.150 + return rwlock; 1.151 + 1.152 +failed: 1.153 + if (rwlock->rw_reader_waitq != NULL) { 1.154 + PR_DestroyCondVar(rwlock->rw_reader_waitq); 1.155 + } 1.156 + if (rwlock->rw_lock != NULL) { 1.157 + PR_DestroyLock(rwlock->rw_lock); 1.158 + } 1.159 + PR_Free(rwlock->rw_name); 1.160 + PR_DELETE(rwlock); 1.161 + return NULL; 1.162 +#endif 1.163 +} 1.164 + 1.165 +/* 1.166 +** Destroy the given RWLock "lock". 1.167 +*/ 1.168 +PR_IMPLEMENT(void) 1.169 +PR_DestroyRWLock(PRRWLock *rwlock) 1.170 +{ 1.171 +#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) 1.172 + int err; 1.173 + err = RWLOCK_DESTROY(&rwlock->rw_lock); 1.174 + PR_ASSERT(err == 0); 1.175 +#else 1.176 + PR_ASSERT(rwlock->rw_reader_cnt == 0); 1.177 + PR_DestroyCondVar(rwlock->rw_reader_waitq); 1.178 + PR_DestroyCondVar(rwlock->rw_writer_waitq); 1.179 + PR_DestroyLock(rwlock->rw_lock); 1.180 +#endif 1.181 + if (rwlock->rw_name != NULL) 1.182 + PR_Free(rwlock->rw_name); 1.183 + PR_DELETE(rwlock); 1.184 +} 1.185 + 1.186 +/* 1.187 +** Read-lock the RWLock. 1.188 +*/ 1.189 +PR_IMPLEMENT(void) 1.190 +PR_RWLock_Rlock(PRRWLock *rwlock) 1.191 +{ 1.192 +#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) 1.193 +int err; 1.194 +#endif 1.195 + 1.196 +#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG 1.197 + /* 1.198 + * assert that rank ordering is not violated; the rank of 'rwlock' should 1.199 + * be equal to or greater than the highest rank of all the locks held by 1.200 + * the thread. 1.201 + */ 1.202 + PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) || 1.203 + (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK())); 1.204 +#endif 1.205 + 1.206 +#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) 1.207 + err = RWLOCK_RDLOCK(&rwlock->rw_lock); 1.208 + PR_ASSERT(err == 0); 1.209 +#else 1.210 + PR_Lock(rwlock->rw_lock); 1.211 + /* 1.212 + * wait if write-locked or if a writer is waiting; preference for writers 1.213 + */ 1.214 + while ((rwlock->rw_lock_cnt < 0) || 1.215 + (rwlock->rw_writer_cnt > 0)) { 1.216 + rwlock->rw_reader_cnt++; 1.217 + PR_WaitCondVar(rwlock->rw_reader_waitq, PR_INTERVAL_NO_TIMEOUT); 1.218 + rwlock->rw_reader_cnt--; 1.219 + } 1.220 + /* 1.221 + * Increment read-lock count 1.222 + */ 1.223 + rwlock->rw_lock_cnt++; 1.224 + 1.225 + PR_Unlock(rwlock->rw_lock); 1.226 +#endif 1.227 + 1.228 +#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG 1.229 + /* 1.230 + * update thread's lock rank 1.231 + */ 1.232 + if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) 1.233 + _PR_SET_THREAD_RWLOCK_RANK(rwlock); 1.234 +#endif 1.235 +} 1.236 + 1.237 +/* 1.238 +** Write-lock the RWLock. 1.239 +*/ 1.240 +PR_IMPLEMENT(void) 1.241 +PR_RWLock_Wlock(PRRWLock *rwlock) 1.242 +{ 1.243 +#if defined(DEBUG) 1.244 +PRThread *me = PR_GetCurrentThread(); 1.245 +#endif 1.246 +#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) 1.247 +int err; 1.248 +#endif 1.249 + 1.250 +#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG 1.251 + /* 1.252 + * assert that rank ordering is not violated; the rank of 'rwlock' should 1.253 + * be equal to or greater than the highest rank of all the locks held by 1.254 + * the thread. 1.255 + */ 1.256 + PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) || 1.257 + (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK())); 1.258 +#endif 1.259 + 1.260 +#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) 1.261 + err = RWLOCK_WRLOCK(&rwlock->rw_lock); 1.262 + PR_ASSERT(err == 0); 1.263 +#else 1.264 + PR_Lock(rwlock->rw_lock); 1.265 + /* 1.266 + * wait if read locked 1.267 + */ 1.268 + while (rwlock->rw_lock_cnt != 0) { 1.269 + rwlock->rw_writer_cnt++; 1.270 + PR_WaitCondVar(rwlock->rw_writer_waitq, PR_INTERVAL_NO_TIMEOUT); 1.271 + rwlock->rw_writer_cnt--; 1.272 + } 1.273 + /* 1.274 + * apply write lock 1.275 + */ 1.276 + rwlock->rw_lock_cnt--; 1.277 + PR_ASSERT(rwlock->rw_lock_cnt == -1); 1.278 +#ifdef DEBUG 1.279 + PR_ASSERT(me != NULL); 1.280 + rwlock->rw_owner = me; 1.281 +#endif 1.282 + PR_Unlock(rwlock->rw_lock); 1.283 +#endif 1.284 + 1.285 +#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG 1.286 + /* 1.287 + * update thread's lock rank 1.288 + */ 1.289 + if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) 1.290 + _PR_SET_THREAD_RWLOCK_RANK(rwlock); 1.291 +#endif 1.292 +} 1.293 + 1.294 +/* 1.295 +** Unlock the RW lock. 1.296 +*/ 1.297 +PR_IMPLEMENT(void) 1.298 +PR_RWLock_Unlock(PRRWLock *rwlock) 1.299 +{ 1.300 +#if defined(DEBUG) 1.301 +PRThread *me = PR_GetCurrentThread(); 1.302 +#endif 1.303 +#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) 1.304 +int err; 1.305 +#endif 1.306 + 1.307 +#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) 1.308 + err = RWLOCK_UNLOCK(&rwlock->rw_lock); 1.309 + PR_ASSERT(err == 0); 1.310 +#else 1.311 + PR_Lock(rwlock->rw_lock); 1.312 + /* 1.313 + * lock must be read or write-locked 1.314 + */ 1.315 + PR_ASSERT(rwlock->rw_lock_cnt != 0); 1.316 + if (rwlock->rw_lock_cnt > 0) { 1.317 + 1.318 + /* 1.319 + * decrement read-lock count 1.320 + */ 1.321 + rwlock->rw_lock_cnt--; 1.322 + if (rwlock->rw_lock_cnt == 0) { 1.323 + /* 1.324 + * lock is not read-locked anymore; wakeup a waiting writer 1.325 + */ 1.326 + if (rwlock->rw_writer_cnt > 0) 1.327 + PR_NotifyCondVar(rwlock->rw_writer_waitq); 1.328 + } 1.329 + } else { 1.330 + PR_ASSERT(rwlock->rw_lock_cnt == -1); 1.331 + 1.332 + rwlock->rw_lock_cnt = 0; 1.333 +#ifdef DEBUG 1.334 + PR_ASSERT(rwlock->rw_owner == me); 1.335 + rwlock->rw_owner = NULL; 1.336 +#endif 1.337 + /* 1.338 + * wakeup a writer, if present; preference for writers 1.339 + */ 1.340 + if (rwlock->rw_writer_cnt > 0) 1.341 + PR_NotifyCondVar(rwlock->rw_writer_waitq); 1.342 + /* 1.343 + * else, wakeup all readers, if any 1.344 + */ 1.345 + else if (rwlock->rw_reader_cnt > 0) 1.346 + PR_NotifyAllCondVar(rwlock->rw_reader_waitq); 1.347 + } 1.348 + PR_Unlock(rwlock->rw_lock); 1.349 +#endif 1.350 + 1.351 +#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG 1.352 + /* 1.353 + * update thread's lock rank 1.354 + */ 1.355 + if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) 1.356 + _PR_UNSET_THREAD_RWLOCK_RANK(rwlock); 1.357 +#endif 1.358 + return; 1.359 +} 1.360 + 1.361 +#ifndef _PR_RWLOCK_RANK_ORDER_DEBUG 1.362 + 1.363 +void _PR_InitRWLocks(void) { } 1.364 + 1.365 +#else 1.366 + 1.367 +void _PR_InitRWLocks(void) 1.368 +{ 1.369 + /* 1.370 + * allocated thread-private-data index for rwlock list 1.371 + */ 1.372 + if (PR_NewThreadPrivateIndex(&pr_thread_rwlock_key, 1.373 + _PR_RELEASE_LOCK_STACK) == PR_FAILURE) { 1.374 + pr_thread_rwlock_alloc_failed = 1; 1.375 + return; 1.376 + } 1.377 +} 1.378 + 1.379 +/* 1.380 + * _PR_SET_THREAD_RWLOCK_RANK 1.381 + * Set a thread's lock rank, which is the highest of the ranks of all 1.382 + * the locks held by the thread. Pointers to the locks are added to a 1.383 + * per-thread list, which is anchored off a thread-private data key. 1.384 + */ 1.385 + 1.386 +static void 1.387 +_PR_SET_THREAD_RWLOCK_RANK(PRRWLock *rwlock) 1.388 +{ 1.389 +thread_rwlock_stack *lock_stack; 1.390 +PRStatus rv; 1.391 + 1.392 + /* 1.393 + * allocate a lock stack 1.394 + */ 1.395 + if ((lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key)) == NULL) { 1.396 + lock_stack = (thread_rwlock_stack *) 1.397 + PR_CALLOC(1 * sizeof(thread_rwlock_stack)); 1.398 + if (lock_stack) { 1.399 + rv = PR_SetThreadPrivate(pr_thread_rwlock_key, lock_stack); 1.400 + if (rv == PR_FAILURE) { 1.401 + PR_DELETE(lock_stack); 1.402 + pr_thread_rwlock_alloc_failed = 1; 1.403 + return; 1.404 + } 1.405 + } else { 1.406 + pr_thread_rwlock_alloc_failed = 1; 1.407 + return; 1.408 + } 1.409 + } 1.410 + /* 1.411 + * add rwlock to lock stack, if limit is not exceeded 1.412 + */ 1.413 + if (lock_stack) { 1.414 + if (lock_stack->trs_index < _PR_RWLOCK_RANK_ORDER_LIMIT) 1.415 + lock_stack->trs_stack[lock_stack->trs_index++] = rwlock; 1.416 + } 1.417 +} 1.418 + 1.419 +static void 1.420 +_PR_RELEASE_LOCK_STACK(void *lock_stack) 1.421 +{ 1.422 + PR_ASSERT(lock_stack); 1.423 + PR_DELETE(lock_stack); 1.424 +} 1.425 + 1.426 +/* 1.427 + * _PR_GET_THREAD_RWLOCK_RANK 1.428 + * 1.429 + * return thread's lock rank. If thread-private-data for the lock 1.430 + * stack is not allocated, return PR_RWLOCK_RANK_NONE. 1.431 + */ 1.432 + 1.433 +static PRUint32 1.434 +_PR_GET_THREAD_RWLOCK_RANK(void) 1.435 +{ 1.436 + thread_rwlock_stack *lock_stack; 1.437 + 1.438 + lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key); 1.439 + if (lock_stack == NULL || lock_stack->trs_index == 0) 1.440 + return (PR_RWLOCK_RANK_NONE); 1.441 + else 1.442 + return(lock_stack->trs_stack[lock_stack->trs_index - 1]->rw_rank); 1.443 +} 1.444 + 1.445 +/* 1.446 + * _PR_UNSET_THREAD_RWLOCK_RANK 1.447 + * 1.448 + * remove the rwlock from the lock stack. Since locks may not be 1.449 + * unlocked in a FIFO order, the entire lock stack is searched. 1.450 + */ 1.451 + 1.452 +static void 1.453 +_PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock *rwlock) 1.454 +{ 1.455 + thread_rwlock_stack *lock_stack; 1.456 + int new_index = 0, index, done = 0; 1.457 + 1.458 + lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key); 1.459 + 1.460 + PR_ASSERT(lock_stack != NULL); 1.461 + 1.462 + for (index = lock_stack->trs_index - 1; index >= 0; index--) { 1.463 + if (!done && (lock_stack->trs_stack[index] == rwlock)) { 1.464 + /* 1.465 + * reset the slot for rwlock 1.466 + */ 1.467 + lock_stack->trs_stack[index] = NULL; 1.468 + done = 1; 1.469 + } 1.470 + /* 1.471 + * search for the lowest-numbered empty slot, above which there are 1.472 + * no non-empty slots 1.473 + */ 1.474 + if (!new_index && (lock_stack->trs_stack[index] != NULL)) 1.475 + new_index = index + 1; 1.476 + if (done && new_index) 1.477 + break; 1.478 + } 1.479 + /* 1.480 + * set top of stack to highest numbered empty slot 1.481 + */ 1.482 + lock_stack->trs_index = new_index; 1.483 + 1.484 +} 1.485 + 1.486 +#endif /* _PR_RWLOCK_RANK_ORDER_DEBUG */