| OLD | NEW |
| (Empty) |
| 1 /* This Source Code Form is subject to the terms of the Mozilla Public | |
| 2 * License, v. 2.0. If a copy of the MPL was not distributed with this | |
| 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
| 4 | |
| 5 #include "nssrwlk.h" | |
| 6 #include "nspr.h" | |
| 7 | |
| 8 PR_BEGIN_EXTERN_C | |
| 9 | |
| 10 /* | |
| 11 * Reader-writer lock | |
| 12 */ | |
| 13 struct nssRWLockStr { | |
| 14 PZLock * rw_lock; | |
| 15 char * rw_name; /* lock name */ | |
| 16 PRUint32 rw_rank; /* rank of the lock */ | |
| 17 PRInt32 rw_writer_locks; /* == 0, if unlocked */ | |
| 18 PRInt32 rw_reader_locks; /* == 0, if unlocked */ | |
| 19 /* > 0 , # of read locks */ | |
| 20 PRUint32 rw_waiting_readers; /* number of waiting readers */ | |
| 21 PRUint32 rw_waiting_writers; /* number of waiting writers */ | |
| 22 PZCondVar * rw_reader_waitq; /* cvar for readers */ | |
| 23 PZCondVar * rw_writer_waitq; /* cvar for writers */ | |
| 24 PRThread * rw_owner; /* lock owner for write-lock */ | |
| 25 /* Non-null if write lock held. */ | |
| 26 }; | |
| 27 | |
| 28 PR_END_EXTERN_C | |
| 29 | |
| 30 #include <string.h> | |
| 31 | |
| 32 #ifdef DEBUG_RANK_ORDER | |
| 33 #define NSS_RWLOCK_RANK_ORDER_DEBUG /* enable deadlock detection using | |
| 34 rank-order for locks | |
| 35 */ | |
| 36 #endif | |
| 37 | |
| 38 #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG | |
| 39 | |
| 40 static PRUintn nss_thread_rwlock_initialized; | |
| 41 static PRUintn nss_thread_rwlock; /* TPD key for lock stack */ | |
| 42 static PRUintn nss_thread_rwlock_alloc_failed; | |
| 43 | |
| 44 #define _NSS_RWLOCK_RANK_ORDER_LIMIT 10 | |
| 45 | |
| 46 typedef struct thread_rwlock_stack { | |
| 47 PRInt32 trs_index; /* top of stack */ | |
| 48 NSSRWLock *trs_stack[_NSS_RWLOCK_RANK_ORDER_LIMIT]; /* stack of lock | |
| 49 pointers */ | |
| 50 } thread_rwlock_stack; | |
| 51 | |
| 52 /* forward static declarations. */ | |
| 53 static PRUint32 nssRWLock_GetThreadRank(PRThread *me); | |
| 54 static void nssRWLock_SetThreadRank(PRThread *me, NSSRWLock *rwlock); | |
| 55 static void nssRWLock_UnsetThreadRank(PRThread *me, NSSRWLock *rwlock); | |
| 56 static void nssRWLock_ReleaseLockStack(void *lock_stack); | |
| 57 | |
| 58 #endif | |
| 59 | |
| 60 #define UNTIL(x) while(!(x)) | |
| 61 | |
| 62 /* | |
| 63 * Reader/Writer Locks | |
| 64 */ | |
| 65 | |
| 66 /* | |
| 67 * NSSRWLock_New | |
| 68 * Create a reader-writer lock, with the given lock rank and lock name | |
| 69 * | |
| 70 */ | |
| 71 | |
| 72 NSSRWLock * | |
| 73 NSSRWLock_New(PRUint32 lock_rank, const char *lock_name) | |
| 74 { | |
| 75 NSSRWLock *rwlock; | |
| 76 | |
| 77 rwlock = PR_NEWZAP(NSSRWLock); | |
| 78 if (rwlock == NULL) | |
| 79 return NULL; | |
| 80 | |
| 81 rwlock->rw_lock = PZ_NewLock(nssILockRWLock); | |
| 82 if (rwlock->rw_lock == NULL) { | |
| 83 goto loser; | |
| 84 } | |
| 85 rwlock->rw_reader_waitq = PZ_NewCondVar(rwlock->rw_lock); | |
| 86 if (rwlock->rw_reader_waitq == NULL) { | |
| 87 goto loser; | |
| 88 } | |
| 89 rwlock->rw_writer_waitq = PZ_NewCondVar(rwlock->rw_lock); | |
| 90 if (rwlock->rw_writer_waitq == NULL) { | |
| 91 goto loser; | |
| 92 } | |
| 93 if (lock_name != NULL) { | |
| 94 rwlock->rw_name = (char*) PR_Malloc(strlen(lock_name) + 1); | |
| 95 if (rwlock->rw_name == NULL) { | |
| 96 goto loser; | |
| 97 } | |
| 98 strcpy(rwlock->rw_name, lock_name); | |
| 99 } else { | |
| 100 rwlock->rw_name = NULL; | |
| 101 } | |
| 102 rwlock->rw_rank = lock_rank; | |
| 103 rwlock->rw_waiting_readers = 0; | |
| 104 rwlock->rw_waiting_writers = 0; | |
| 105 rwlock->rw_reader_locks = 0; | |
| 106 rwlock->rw_writer_locks = 0; | |
| 107 | |
| 108 return rwlock; | |
| 109 | |
| 110 loser: | |
| 111 NSSRWLock_Destroy(rwlock); | |
| 112 return(NULL); | |
| 113 } | |
| 114 | |
| 115 /* | |
| 116 ** Destroy the given RWLock "lock". | |
| 117 */ | |
| 118 void | |
| 119 NSSRWLock_Destroy(NSSRWLock *rwlock) | |
| 120 { | |
| 121 PR_ASSERT(rwlock != NULL); | |
| 122 PR_ASSERT(rwlock->rw_waiting_readers == 0); | |
| 123 | |
| 124 /* XXX Shouldn't we lock the PZLock before destroying this?? */ | |
| 125 | |
| 126 if (rwlock->rw_name) | |
| 127 PR_Free(rwlock->rw_name); | |
| 128 if (rwlock->rw_reader_waitq) | |
| 129 PZ_DestroyCondVar(rwlock->rw_reader_waitq); | |
| 130 if (rwlock->rw_writer_waitq) | |
| 131 PZ_DestroyCondVar(rwlock->rw_writer_waitq); | |
| 132 if (rwlock->rw_lock) | |
| 133 PZ_DestroyLock(rwlock->rw_lock); | |
| 134 PR_DELETE(rwlock); | |
| 135 } | |
| 136 | |
| 137 /* | |
| 138 ** Read-lock the RWLock. | |
| 139 */ | |
| 140 void | |
| 141 NSSRWLock_LockRead(NSSRWLock *rwlock) | |
| 142 { | |
| 143 PRThread *me = PR_GetCurrentThread(); | |
| 144 | |
| 145 PZ_Lock(rwlock->rw_lock); | |
| 146 #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG | |
| 147 | |
| 148 /* | |
| 149 * assert that rank ordering is not violated; the rank of 'rwlock' should | |
| 150 * be equal to or greater than the highest rank of all the locks held by | |
| 151 * the thread. | |
| 152 */ | |
| 153 PR_ASSERT((rwlock->rw_rank == NSS_RWLOCK_RANK_NONE) || | |
| 154 (rwlock->rw_rank >= nssRWLock_GetThreadRank(me))); | |
| 155 #endif | |
| 156 /* | |
| 157 * wait if write-locked or if a writer is waiting; preference for writers | |
| 158 */ | |
| 159 UNTIL ( (rwlock->rw_owner == me) || /* I own it, or */ | |
| 160 ((rwlock->rw_owner == NULL) && /* no-one owns it, and */ | |
| 161 (rwlock->rw_waiting_writers == 0))) { /* no-one is waiting to own */ | |
| 162 | |
| 163 rwlock->rw_waiting_readers++; | |
| 164 PZ_WaitCondVar(rwlock->rw_reader_waitq, PR_INTERVAL_NO_TIMEOUT); | |
| 165 rwlock->rw_waiting_readers--; | |
| 166 } | |
| 167 rwlock->rw_reader_locks++; /* Increment read-lock count */ | |
| 168 | |
| 169 PZ_Unlock(rwlock->rw_lock); | |
| 170 | |
| 171 #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG | |
| 172 nssRWLock_SetThreadRank(me, rwlock);/* update thread's lock rank */ | |
| 173 #endif | |
| 174 } | |
| 175 | |
| 176 /* Unlock a Read lock held on this RW lock. | |
| 177 */ | |
| 178 void | |
| 179 NSSRWLock_UnlockRead(NSSRWLock *rwlock) | |
| 180 { | |
| 181 PZ_Lock(rwlock->rw_lock); | |
| 182 | |
| 183 PR_ASSERT(rwlock->rw_reader_locks > 0); /* lock must be read locked */ | |
| 184 | |
| 185 if (( rwlock->rw_reader_locks > 0) && /* caller isn't screwey */ | |
| 186 (--rwlock->rw_reader_locks == 0) && /* not read locked any more */ | |
| 187 ( rwlock->rw_owner == NULL) && /* not write locked */ | |
| 188 ( rwlock->rw_waiting_writers > 0)) { /* someone's waiting. */ | |
| 189 | |
| 190 PZ_NotifyCondVar(rwlock->rw_writer_waitq); /* wake him up. */ | |
| 191 } | |
| 192 | |
| 193 PZ_Unlock(rwlock->rw_lock); | |
| 194 | |
| 195 #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG | |
| 196 /* | |
| 197 * update thread's lock rank | |
| 198 */ | |
| 199 nssRWLock_UnsetThreadRank(me, rwlock); | |
| 200 #endif | |
| 201 return; | |
| 202 } | |
| 203 | |
| 204 /* | |
| 205 ** Write-lock the RWLock. | |
| 206 */ | |
| 207 void | |
| 208 NSSRWLock_LockWrite(NSSRWLock *rwlock) | |
| 209 { | |
| 210 PRThread *me = PR_GetCurrentThread(); | |
| 211 | |
| 212 PZ_Lock(rwlock->rw_lock); | |
| 213 #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG | |
| 214 /* | |
| 215 * assert that rank ordering is not violated; the rank of 'rwlock' should | |
| 216 * be equal to or greater than the highest rank of all the locks held by | |
| 217 * the thread. | |
| 218 */ | |
| 219 PR_ASSERT((rwlock->rw_rank == NSS_RWLOCK_RANK_NONE) || | |
| 220 (rwlock->rw_rank >= nssRWLock_GetThreadRank(me))); | |
| 221 #endif | |
| 222 /* | |
| 223 * wait if read locked or write locked. | |
| 224 */ | |
| 225 PR_ASSERT(rwlock->rw_reader_locks >= 0); | |
| 226 PR_ASSERT(me != NULL); | |
| 227 | |
| 228 UNTIL ( (rwlock->rw_owner == me) || /* I own write lock, or */ | |
| 229 ((rwlock->rw_owner == NULL) && /* no writer and */ | |
| 230 (rwlock->rw_reader_locks == 0))) { /* no readers, either. */ | |
| 231 | |
| 232 rwlock->rw_waiting_writers++; | |
| 233 PZ_WaitCondVar(rwlock->rw_writer_waitq, PR_INTERVAL_NO_TIMEOUT); | |
| 234 rwlock->rw_waiting_writers--; | |
| 235 PR_ASSERT(rwlock->rw_reader_locks >= 0); | |
| 236 } | |
| 237 | |
| 238 PR_ASSERT(rwlock->rw_reader_locks == 0); | |
| 239 /* | |
| 240 * apply write lock | |
| 241 */ | |
| 242 rwlock->rw_owner = me; | |
| 243 rwlock->rw_writer_locks++; /* Increment write-lock count */ | |
| 244 | |
| 245 PZ_Unlock(rwlock->rw_lock); | |
| 246 | |
| 247 #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG | |
| 248 /* | |
| 249 * update thread's lock rank | |
| 250 */ | |
| 251 nssRWLock_SetThreadRank(me,rwlock); | |
| 252 #endif | |
| 253 } | |
| 254 | |
| 255 /* Unlock a Read lock held on this RW lock. | |
| 256 */ | |
| 257 void | |
| 258 NSSRWLock_UnlockWrite(NSSRWLock *rwlock) | |
| 259 { | |
| 260 PRThread *me = PR_GetCurrentThread(); | |
| 261 | |
| 262 PZ_Lock(rwlock->rw_lock); | |
| 263 PR_ASSERT(rwlock->rw_owner == me); /* lock must be write-locked by me. */ | |
| 264 PR_ASSERT(rwlock->rw_writer_locks > 0); /* lock must be write locked */ | |
| 265 | |
| 266 if ( rwlock->rw_owner == me && /* I own it, and */ | |
| 267 rwlock->rw_writer_locks > 0 && /* I own it, and */ | |
| 268 --rwlock->rw_writer_locks == 0) { /* I'm all done with it */ | |
| 269 | |
| 270 rwlock->rw_owner = NULL; /* I don't own it any more. */ | |
| 271 | |
| 272 /* Give preference to waiting writers. */ | |
| 273 if (rwlock->rw_waiting_writers > 0) { | |
| 274 if (rwlock->rw_reader_locks == 0) | |
| 275 PZ_NotifyCondVar(rwlock->rw_writer_waitq); | |
| 276 } else if (rwlock->rw_waiting_readers > 0) { | |
| 277 PZ_NotifyAllCondVar(rwlock->rw_reader_waitq); | |
| 278 } | |
| 279 } | |
| 280 PZ_Unlock(rwlock->rw_lock); | |
| 281 | |
| 282 #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG | |
| 283 /* | |
| 284 * update thread's lock rank | |
| 285 */ | |
| 286 nssRWLock_UnsetThreadRank(me, rwlock); | |
| 287 #endif | |
| 288 return; | |
| 289 } | |
| 290 | |
| 291 /* This is primarily for debugging, i.e. for inclusion in ASSERT calls. */ | |
| 292 PRBool | |
| 293 NSSRWLock_HaveWriteLock(NSSRWLock *rwlock) { | |
| 294 PRBool ownWriteLock; | |
| 295 PRThread *me = PR_GetCurrentThread(); | |
| 296 | |
| 297 /* This lock call isn't really necessary. | |
| 298 ** If this thread is the owner, that fact cannot change during this call, | |
| 299 ** because this thread is in this call. | |
| 300 ** If this thread is NOT the owner, the owner could change, but it | |
| 301 ** could not become this thread. | |
| 302 */ | |
| 303 #if UNNECESSARY | |
| 304 PZ_Lock(rwlock->rw_lock); | |
| 305 #endif | |
| 306 ownWriteLock = (PRBool)(me == rwlock->rw_owner); | |
| 307 #if UNNECESSARY | |
| 308 PZ_Unlock(rwlock->rw_lock); | |
| 309 #endif | |
| 310 return ownWriteLock; | |
| 311 } | |
| 312 | |
| 313 #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG | |
| 314 | |
| 315 /* | |
| 316 * nssRWLock_SetThreadRank | |
| 317 * Set a thread's lock rank, which is the highest of the ranks of all | |
| 318 * the locks held by the thread. Pointers to the locks are added to a | |
| 319 * per-thread list, which is anchored off a thread-private data key. | |
| 320 */ | |
| 321 | |
| 322 static void | |
| 323 nssRWLock_SetThreadRank(PRThread *me, NSSRWLock *rwlock) | |
| 324 { | |
| 325 thread_rwlock_stack *lock_stack; | |
| 326 PRStatus rv; | |
| 327 | |
| 328 /* | |
| 329 * allocated thread-private-data for rwlock list, if not already allocated | |
| 330 */ | |
| 331 if (!nss_thread_rwlock_initialized) { | |
| 332 /* | |
| 333 * allocate tpd, only if not failed already | |
| 334 */ | |
| 335 if (!nss_thread_rwlock_alloc_failed) { | |
| 336 if (PR_NewThreadPrivateIndex(&nss_thread_rwlock, | |
| 337 nssRWLock_ReleaseLockStack) | |
| 338 == PR_FAILURE) { | |
| 339 nss_thread_rwlock_alloc_failed = 1; | |
| 340 return; | |
| 341 } | |
| 342 } else | |
| 343 return; | |
| 344 } | |
| 345 /* | |
| 346 * allocate a lock stack | |
| 347 */ | |
| 348 if ((lock_stack = PR_GetThreadPrivate(nss_thread_rwlock)) == NULL) { | |
| 349 lock_stack = (thread_rwlock_stack *) | |
| 350 PR_CALLOC(1 * sizeof(thread_rwlock_stack)); | |
| 351 if (lock_stack) { | |
| 352 rv = PR_SetThreadPrivate(nss_thread_rwlock, lock_stack); | |
| 353 if (rv == PR_FAILURE) { | |
| 354 PR_DELETE(lock_stack); | |
| 355 nss_thread_rwlock_alloc_failed = 1; | |
| 356 return; | |
| 357 } | |
| 358 } else { | |
| 359 nss_thread_rwlock_alloc_failed = 1; | |
| 360 return; | |
| 361 } | |
| 362 } | |
| 363 /* | |
| 364 * add rwlock to lock stack, if limit is not exceeded | |
| 365 */ | |
| 366 if (lock_stack) { | |
| 367 if (lock_stack->trs_index < _NSS_RWLOCK_RANK_ORDER_LIMIT) | |
| 368 lock_stack->trs_stack[lock_stack->trs_index++] = rwlock; | |
| 369 } | |
| 370 nss_thread_rwlock_initialized = 1; | |
| 371 } | |
| 372 | |
| 373 static void | |
| 374 nssRWLock_ReleaseLockStack(void *lock_stack) | |
| 375 { | |
| 376 PR_ASSERT(lock_stack); | |
| 377 PR_DELETE(lock_stack); | |
| 378 } | |
| 379 | |
| 380 /* | |
| 381 * nssRWLock_GetThreadRank | |
| 382 * | |
| 383 * return thread's lock rank. If thread-private-data for the lock | |
| 384 * stack is not allocated, return NSS_RWLOCK_RANK_NONE. | |
| 385 */ | |
| 386 | |
| 387 static PRUint32 | |
| 388 nssRWLock_GetThreadRank(PRThread *me) | |
| 389 { | |
| 390 thread_rwlock_stack *lock_stack; | |
| 391 | |
| 392 if (nss_thread_rwlock_initialized) { | |
| 393 if ((lock_stack = PR_GetThreadPrivate(nss_thread_rwlock)) == NULL) | |
| 394 return (NSS_RWLOCK_RANK_NONE); | |
| 395 else | |
| 396 return(lock_stack->trs_stack[lock_stack->trs_index - 1]->rw_rank); | |
| 397 | |
| 398 } else | |
| 399 return (NSS_RWLOCK_RANK_NONE); | |
| 400 } | |
| 401 | |
| 402 /* | |
| 403 * nssRWLock_UnsetThreadRank | |
| 404 * | |
| 405 * remove the rwlock from the lock stack. Since locks may not be | |
| 406 * unlocked in a FIFO order, the entire lock stack is searched. | |
| 407 */ | |
| 408 | |
| 409 static void | |
| 410 nssRWLock_UnsetThreadRank(PRThread *me, NSSRWLock *rwlock) | |
| 411 { | |
| 412 thread_rwlock_stack *lock_stack; | |
| 413 int new_index = 0, index, done = 0; | |
| 414 | |
| 415 if (!nss_thread_rwlock_initialized) | |
| 416 return; | |
| 417 | |
| 418 lock_stack = PR_GetThreadPrivate(nss_thread_rwlock); | |
| 419 | |
| 420 PR_ASSERT(lock_stack != NULL); | |
| 421 | |
| 422 index = lock_stack->trs_index - 1; | |
| 423 while (index-- >= 0) { | |
| 424 if ((lock_stack->trs_stack[index] == rwlock) && !done) { | |
| 425 /* | |
| 426 * reset the slot for rwlock | |
| 427 */ | |
| 428 lock_stack->trs_stack[index] = NULL; | |
| 429 done = 1; | |
| 430 } | |
| 431 /* | |
| 432 * search for the lowest-numbered empty slot, above which there are | |
| 433 * no non-empty slots | |
| 434 */ | |
| 435 if ((lock_stack->trs_stack[index] != NULL) && !new_index) | |
| 436 new_index = index + 1; | |
| 437 if (done && new_index) | |
| 438 break; | |
| 439 } | |
| 440 /* | |
| 441 * set top of stack to highest numbered empty slot | |
| 442 */ | |
| 443 lock_stack->trs_index = new_index; | |
| 444 | |
| 445 } | |
| 446 | |
| 447 #endif /* NSS_RWLOCK_RANK_ORDER_DEBUG */ | |
| OLD | NEW |