prrwlock.c (11790B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "primpl.h" 7 8 #include <string.h> 9 10 #if defined(SOLARIS) && \ 11 (defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY)) 12 13 # include <synch.h> 14 # define HAVE_UI_RWLOCK 15 # define RWLOCK_T rwlock_t 16 # define RWLOCK_INIT(lock) rwlock_init(lock, USYNC_THREAD, NULL) 17 # define RWLOCK_DESTROY(lock) rwlock_destroy(lock) 18 # define RWLOCK_RDLOCK(lock) rw_rdlock(lock) 19 # define RWLOCK_WRLOCK(lock) rw_wrlock(lock) 20 # define RWLOCK_UNLOCK(lock) rw_unlock(lock) 21 22 #endif 23 24 /* 25 * Reader-writer lock 26 */ 27 struct PRRWLock { 28 char* rw_name; /* lock name */ 29 PRUint32 rw_rank; /* rank of the lock */ 30 31 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) 32 RWLOCK_T rw_lock; 33 #else 34 PRLock* rw_lock; 35 PRInt32 rw_lock_cnt; /* == 0, if unlocked */ 36 /* == -1, if write-locked */ 37 /* > 0 , # of read locks */ 38 PRUint32 rw_reader_cnt; /* number of waiting readers */ 39 PRUint32 rw_writer_cnt; /* number of waiting writers */ 40 PRCondVar* rw_reader_waitq; /* cvar for readers */ 41 PRCondVar* rw_writer_waitq; /* cvar for writers */ 42 # ifdef DEBUG 43 PRThread* rw_owner; /* lock owner for write-lock */ 44 # endif 45 #endif 46 }; 47 48 #ifdef DEBUG 49 # define _PR_RWLOCK_RANK_ORDER_DEBUG /* enable deadlock detection using \ 50 rank-order for locks \ 51 */ 52 #endif 53 54 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG 55 56 static PRUintn pr_thread_rwlock_key; /* TPD key for lock stack */ 57 static PRUintn pr_thread_rwlock_alloc_failed; 58 59 # define _PR_RWLOCK_RANK_ORDER_LIMIT 10 60 61 typedef struct thread_rwlock_stack { 62 PRInt32 trs_index; /* top of stack */ 63 PRRWLock* trs_stack[_PR_RWLOCK_RANK_ORDER_LIMIT]; /* stack of lock 64 pointers */ 65 66 } thread_rwlock_stack; 67 68 static void _PR_SET_THREAD_RWLOCK_RANK(PRRWLock* rwlock); 69 static PRUint32 _PR_GET_THREAD_RWLOCK_RANK(void); 70 static void _PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock* rwlock); 71 static void _PR_RELEASE_LOCK_STACK(void* lock_stack); 72 73 #endif 74 75 /* 76 * Reader/Writer Locks 77 */ 78 79 /* 80 * PR_NewRWLock 81 * Create a reader-writer lock, with the given lock rank and lock name 82 * 83 */ 84 85 PR_IMPLEMENT(PRRWLock*) 86 PR_NewRWLock(PRUint32 lock_rank, const char* lock_name) { 87 PRRWLock* rwlock; 88 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) 89 int err; 90 #endif 91 92 if (!_pr_initialized) { 93 _PR_ImplicitInitialization(); 94 } 95 96 rwlock = PR_NEWZAP(PRRWLock); 97 if (rwlock == NULL) { 98 return NULL; 99 } 100 101 rwlock->rw_rank = lock_rank; 102 if (lock_name != NULL) { 103 rwlock->rw_name = (char*)PR_Malloc(strlen(lock_name) + 1); 104 if (rwlock->rw_name == NULL) { 105 PR_DELETE(rwlock); 106 return (NULL); 107 } 108 strcpy(rwlock->rw_name, lock_name); 109 } else { 110 rwlock->rw_name = NULL; 111 } 112 113 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) 114 err = RWLOCK_INIT(&rwlock->rw_lock); 115 if (err != 0) { 116 PR_SetError(PR_UNKNOWN_ERROR, err); 117 PR_Free(rwlock->rw_name); 118 PR_DELETE(rwlock); 119 return NULL; 120 } 121 return rwlock; 122 #else 123 rwlock->rw_lock = PR_NewLock(); 124 if (rwlock->rw_lock == NULL) { 125 goto failed; 126 } 127 rwlock->rw_reader_waitq = PR_NewCondVar(rwlock->rw_lock); 128 if (rwlock->rw_reader_waitq == NULL) { 129 goto failed; 130 } 131 rwlock->rw_writer_waitq = PR_NewCondVar(rwlock->rw_lock); 132 if (rwlock->rw_writer_waitq == NULL) { 133 goto failed; 134 } 135 rwlock->rw_reader_cnt = 0; 136 rwlock->rw_writer_cnt = 0; 137 rwlock->rw_lock_cnt = 0; 138 return rwlock; 139 140 failed: 141 if (rwlock->rw_reader_waitq != NULL) { 142 PR_DestroyCondVar(rwlock->rw_reader_waitq); 143 } 144 if (rwlock->rw_lock != NULL) { 145 PR_DestroyLock(rwlock->rw_lock); 146 } 147 PR_Free(rwlock->rw_name); 148 PR_DELETE(rwlock); 149 return NULL; 150 #endif 151 } 152 153 /* 154 ** Destroy the given RWLock "lock". 155 */ 156 PR_IMPLEMENT(void) 157 PR_DestroyRWLock(PRRWLock* rwlock) { 158 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) 159 int err; 160 err = RWLOCK_DESTROY(&rwlock->rw_lock); 161 PR_ASSERT(err == 0); 162 #else 163 PR_ASSERT(rwlock->rw_reader_cnt == 0); 164 PR_DestroyCondVar(rwlock->rw_reader_waitq); 165 PR_DestroyCondVar(rwlock->rw_writer_waitq); 166 PR_DestroyLock(rwlock->rw_lock); 167 #endif 168 if (rwlock->rw_name != NULL) { 169 PR_Free(rwlock->rw_name); 170 } 171 PR_DELETE(rwlock); 172 } 173 174 /* 175 ** Read-lock the RWLock. 176 */ 177 PR_IMPLEMENT(void) 178 PR_RWLock_Rlock(PRRWLock* rwlock) { 179 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) 180 int err; 181 #endif 182 183 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG 184 /* 185 * assert that rank ordering is not violated; the rank of 'rwlock' should 186 * be equal to or greater than the highest rank of all the locks held by 187 * the thread. 188 */ 189 PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) || 190 (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK())); 191 #endif 192 193 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) 194 err = RWLOCK_RDLOCK(&rwlock->rw_lock); 195 PR_ASSERT(err == 0); 196 #else 197 PR_Lock(rwlock->rw_lock); 198 /* 199 * wait if write-locked or if a writer is waiting; preference for writers 200 */ 201 while ((rwlock->rw_lock_cnt < 0) || (rwlock->rw_writer_cnt > 0)) { 202 rwlock->rw_reader_cnt++; 203 PR_WaitCondVar(rwlock->rw_reader_waitq, PR_INTERVAL_NO_TIMEOUT); 204 rwlock->rw_reader_cnt--; 205 } 206 /* 207 * Increment read-lock count 208 */ 209 rwlock->rw_lock_cnt++; 210 211 PR_Unlock(rwlock->rw_lock); 212 #endif 213 214 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG 215 /* 216 * update thread's lock rank 217 */ 218 if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) { 219 _PR_SET_THREAD_RWLOCK_RANK(rwlock); 220 } 221 #endif 222 } 223 224 /* 225 ** Write-lock the RWLock. 226 */ 227 PR_IMPLEMENT(void) 228 PR_RWLock_Wlock(PRRWLock* rwlock) { 229 #if defined(DEBUG) 230 PRThread* me = PR_GetCurrentThread(); 231 #endif 232 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) 233 int err; 234 #endif 235 236 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG 237 /* 238 * assert that rank ordering is not violated; the rank of 'rwlock' should 239 * be equal to or greater than the highest rank of all the locks held by 240 * the thread. 241 */ 242 PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) || 243 (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK())); 244 #endif 245 246 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) 247 err = RWLOCK_WRLOCK(&rwlock->rw_lock); 248 PR_ASSERT(err == 0); 249 #else 250 PR_Lock(rwlock->rw_lock); 251 /* 252 * wait if read locked 253 */ 254 while (rwlock->rw_lock_cnt != 0) { 255 rwlock->rw_writer_cnt++; 256 PR_WaitCondVar(rwlock->rw_writer_waitq, PR_INTERVAL_NO_TIMEOUT); 257 rwlock->rw_writer_cnt--; 258 } 259 /* 260 * apply write lock 261 */ 262 rwlock->rw_lock_cnt--; 263 PR_ASSERT(rwlock->rw_lock_cnt == -1); 264 # ifdef DEBUG 265 PR_ASSERT(me != NULL); 266 rwlock->rw_owner = me; 267 # endif 268 PR_Unlock(rwlock->rw_lock); 269 #endif 270 271 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG 272 /* 273 * update thread's lock rank 274 */ 275 if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) { 276 _PR_SET_THREAD_RWLOCK_RANK(rwlock); 277 } 278 #endif 279 } 280 281 /* 282 ** Unlock the RW lock. 283 */ 284 PR_IMPLEMENT(void) 285 PR_RWLock_Unlock(PRRWLock* rwlock) { 286 #if defined(DEBUG) 287 PRThread* me = PR_GetCurrentThread(); 288 #endif 289 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) 290 int err; 291 #endif 292 293 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) 294 err = RWLOCK_UNLOCK(&rwlock->rw_lock); 295 PR_ASSERT(err == 0); 296 #else 297 PR_Lock(rwlock->rw_lock); 298 /* 299 * lock must be read or write-locked 300 */ 301 PR_ASSERT(rwlock->rw_lock_cnt != 0); 302 if (rwlock->rw_lock_cnt > 0) { 303 /* 304 * decrement read-lock count 305 */ 306 rwlock->rw_lock_cnt--; 307 if (rwlock->rw_lock_cnt == 0) { 308 /* 309 * lock is not read-locked anymore; wakeup a waiting writer 310 */ 311 if (rwlock->rw_writer_cnt > 0) { 312 PR_NotifyCondVar(rwlock->rw_writer_waitq); 313 } 314 } 315 } else { 316 PR_ASSERT(rwlock->rw_lock_cnt == -1); 317 318 rwlock->rw_lock_cnt = 0; 319 # ifdef DEBUG 320 PR_ASSERT(rwlock->rw_owner == me); 321 rwlock->rw_owner = NULL; 322 # endif 323 /* 324 * wakeup a writer, if present; preference for writers 325 */ 326 if (rwlock->rw_writer_cnt > 0) { 327 PR_NotifyCondVar(rwlock->rw_writer_waitq); 328 } 329 /* 330 * else, wakeup all readers, if any 331 */ 332 else if (rwlock->rw_reader_cnt > 0) { 333 PR_NotifyAllCondVar(rwlock->rw_reader_waitq); 334 } 335 } 336 PR_Unlock(rwlock->rw_lock); 337 #endif 338 339 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG 340 /* 341 * update thread's lock rank 342 */ 343 if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) { 344 _PR_UNSET_THREAD_RWLOCK_RANK(rwlock); 345 } 346 #endif 347 return; 348 } 349 350 #ifndef _PR_RWLOCK_RANK_ORDER_DEBUG 351 352 void _PR_InitRWLocks(void) {} 353 354 #else 355 356 void _PR_InitRWLocks(void) { 357 /* 358 * allocated thread-private-data index for rwlock list 359 */ 360 if (PR_NewThreadPrivateIndex(&pr_thread_rwlock_key, _PR_RELEASE_LOCK_STACK) == 361 PR_FAILURE) { 362 pr_thread_rwlock_alloc_failed = 1; 363 return; 364 } 365 } 366 367 /* 368 * _PR_SET_THREAD_RWLOCK_RANK 369 * Set a thread's lock rank, which is the highest of the ranks of all 370 * the locks held by the thread. Pointers to the locks are added to a 371 * per-thread list, which is anchored off a thread-private data key. 372 */ 373 374 static void _PR_SET_THREAD_RWLOCK_RANK(PRRWLock* rwlock) { 375 thread_rwlock_stack* lock_stack; 376 PRStatus rv; 377 378 /* 379 * allocate a lock stack 380 */ 381 if ((lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key)) == NULL) { 382 lock_stack = 383 (thread_rwlock_stack*)PR_CALLOC(1 * sizeof(thread_rwlock_stack)); 384 if (lock_stack) { 385 rv = PR_SetThreadPrivate(pr_thread_rwlock_key, lock_stack); 386 if (rv == PR_FAILURE) { 387 PR_DELETE(lock_stack); 388 pr_thread_rwlock_alloc_failed = 1; 389 return; 390 } 391 } else { 392 pr_thread_rwlock_alloc_failed = 1; 393 return; 394 } 395 } 396 /* 397 * add rwlock to lock stack, if limit is not exceeded 398 */ 399 if (lock_stack) { 400 if (lock_stack->trs_index < _PR_RWLOCK_RANK_ORDER_LIMIT) { 401 lock_stack->trs_stack[lock_stack->trs_index++] = rwlock; 402 } 403 } 404 } 405 406 static void _PR_RELEASE_LOCK_STACK(void* lock_stack) { 407 PR_ASSERT(lock_stack); 408 PR_DELETE(lock_stack); 409 } 410 411 /* 412 * _PR_GET_THREAD_RWLOCK_RANK 413 * 414 * return thread's lock rank. If thread-private-data for the lock 415 * stack is not allocated, return PR_RWLOCK_RANK_NONE. 416 */ 417 418 static PRUint32 _PR_GET_THREAD_RWLOCK_RANK(void) { 419 thread_rwlock_stack* lock_stack; 420 421 lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key); 422 if (lock_stack == NULL || lock_stack->trs_index == 0) { 423 return (PR_RWLOCK_RANK_NONE); 424 } else { 425 return (lock_stack->trs_stack[lock_stack->trs_index - 1]->rw_rank); 426 } 427 } 428 429 /* 430 * _PR_UNSET_THREAD_RWLOCK_RANK 431 * 432 * remove the rwlock from the lock stack. Since locks may not be 433 * unlocked in a FIFO order, the entire lock stack is searched. 434 */ 435 436 static void _PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock* rwlock) { 437 thread_rwlock_stack* lock_stack; 438 int new_index = 0, index, done = 0; 439 440 lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key); 441 442 PR_ASSERT(lock_stack != NULL); 443 444 for (index = lock_stack->trs_index - 1; index >= 0; index--) { 445 if (!done && (lock_stack->trs_stack[index] == rwlock)) { 446 /* 447 * reset the slot for rwlock 448 */ 449 lock_stack->trs_stack[index] = NULL; 450 done = 1; 451 } 452 /* 453 * search for the lowest-numbered empty slot, above which there are 454 * no non-empty slots 455 */ 456 if (!new_index && (lock_stack->trs_stack[index] != NULL)) { 457 new_index = index + 1; 458 } 459 if (done && new_index) { 460 break; 461 } 462 } 463 /* 464 * set top of stack to highest numbered empty slot 465 */ 466 lock_stack->trs_index = new_index; 467 } 468 469 #endif /* _PR_RWLOCK_RANK_ORDER_DEBUG */