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