|  | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | 
|  | /* This Source Code Form is subject to the terms of the Mozilla Public | 
|  | * License, v. 2.0. If a copy of the MPL was not distributed with this | 
|  | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | 
|  |  | 
|  | #include "primpl.h" | 
|  |  | 
|  | #include <string.h> | 
|  |  | 
|  | #if defined(HPUX) && defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) | 
|  |  | 
|  | #include <pthread.h> | 
|  | #define HAVE_UNIX98_RWLOCK | 
|  | #define RWLOCK_T pthread_rwlock_t | 
|  | #define RWLOCK_INIT(lock) pthread_rwlock_init(lock, NULL) | 
|  | #define RWLOCK_DESTROY(lock) pthread_rwlock_destroy(lock) | 
|  | #define RWLOCK_RDLOCK(lock) pthread_rwlock_rdlock(lock) | 
|  | #define RWLOCK_WRLOCK(lock) pthread_rwlock_wrlock(lock) | 
|  | #define RWLOCK_UNLOCK(lock) pthread_rwlock_unlock(lock) | 
|  |  | 
|  | #elif defined(SOLARIS) && (defined(_PR_PTHREADS) \ | 
|  | || defined(_PR_GLOBAL_THREADS_ONLY)) | 
|  |  | 
|  | #include <synch.h> | 
|  | #define HAVE_UI_RWLOCK | 
|  | #define RWLOCK_T rwlock_t | 
|  | #define RWLOCK_INIT(lock) rwlock_init(lock, USYNC_THREAD, NULL) | 
|  | #define RWLOCK_DESTROY(lock) rwlock_destroy(lock) | 
|  | #define RWLOCK_RDLOCK(lock) rw_rdlock(lock) | 
|  | #define RWLOCK_WRLOCK(lock) rw_wrlock(lock) | 
|  | #define RWLOCK_UNLOCK(lock) rw_unlock(lock) | 
|  |  | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | * Reader-writer lock | 
|  | */ | 
|  | struct PRRWLock { | 
|  | char			*rw_name;			/* lock name					*/ | 
|  | PRUint32		rw_rank;			/* rank of the lock				*/ | 
|  |  | 
|  | #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) | 
|  | RWLOCK_T		rw_lock; | 
|  | #else | 
|  | PRLock			*rw_lock; | 
|  | PRInt32			rw_lock_cnt;		/* ==  0, if unlocked			*/ | 
|  | /* == -1, if write-locked		*/ | 
|  | /* > 0	, # of read locks		*/ | 
|  | PRUint32		rw_reader_cnt;		/* number of waiting readers	*/ | 
|  | PRUint32		rw_writer_cnt;		/* number of waiting writers	*/ | 
|  | PRCondVar   	*rw_reader_waitq;	/* cvar for readers 			*/ | 
|  | PRCondVar   	*rw_writer_waitq;	/* cvar for writers				*/ | 
|  | #ifdef DEBUG | 
|  | PRThread 		*rw_owner;			/* lock owner for write-lock	*/ | 
|  | #endif | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | #ifdef DEBUG | 
|  | #define _PR_RWLOCK_RANK_ORDER_DEBUG	/* enable deadlock detection using | 
|  | rank-order for locks | 
|  | */ | 
|  | #endif | 
|  |  | 
|  | #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG | 
|  |  | 
|  | static PRUintn	pr_thread_rwlock_key;			/* TPD key for lock stack */ | 
|  | static PRUintn	pr_thread_rwlock_alloc_failed; | 
|  |  | 
|  | #define	_PR_RWLOCK_RANK_ORDER_LIMIT	10 | 
|  |  | 
|  | typedef struct thread_rwlock_stack { | 
|  | PRInt32		trs_index;									/* top of stack */ | 
|  | PRRWLock	*trs_stack[_PR_RWLOCK_RANK_ORDER_LIMIT];	/* stack of lock | 
|  | pointers */ | 
|  |  | 
|  | } thread_rwlock_stack; | 
|  |  | 
|  | static void _PR_SET_THREAD_RWLOCK_RANK(PRRWLock *rwlock); | 
|  | static PRUint32 _PR_GET_THREAD_RWLOCK_RANK(void); | 
|  | static void _PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock *rwlock); | 
|  | static void _PR_RELEASE_LOCK_STACK(void *lock_stack); | 
|  |  | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | * Reader/Writer Locks | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * PR_NewRWLock | 
|  | *		Create a reader-writer lock, with the given lock rank and lock name | 
|  | * | 
|  | */ | 
|  |  | 
|  | PR_IMPLEMENT(PRRWLock *) | 
|  | PR_NewRWLock(PRUint32 lock_rank, const char *lock_name) | 
|  | { | 
|  | PRRWLock *rwlock; | 
|  | #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) | 
|  | int err; | 
|  | #endif | 
|  |  | 
|  | if (!_pr_initialized) _PR_ImplicitInitialization(); | 
|  |  | 
|  | rwlock = PR_NEWZAP(PRRWLock); | 
|  | if (rwlock == NULL) | 
|  | return NULL; | 
|  |  | 
|  | rwlock->rw_rank = lock_rank; | 
|  | if (lock_name != NULL) { | 
|  | rwlock->rw_name = (char*) PR_Malloc(strlen(lock_name) + 1); | 
|  | if (rwlock->rw_name == NULL) { | 
|  | PR_DELETE(rwlock); | 
|  | return(NULL); | 
|  | } | 
|  | strcpy(rwlock->rw_name, lock_name); | 
|  | } else { | 
|  | rwlock->rw_name = NULL; | 
|  | } | 
|  |  | 
|  | #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) | 
|  | err = RWLOCK_INIT(&rwlock->rw_lock); | 
|  | if (err != 0) { | 
|  | PR_SetError(PR_UNKNOWN_ERROR, err); | 
|  | PR_Free(rwlock->rw_name); | 
|  | PR_DELETE(rwlock); | 
|  | return NULL; | 
|  | } | 
|  | return rwlock; | 
|  | #else | 
|  | rwlock->rw_lock = PR_NewLock(); | 
|  | if (rwlock->rw_lock == NULL) { | 
|  | goto failed; | 
|  | } | 
|  | rwlock->rw_reader_waitq = PR_NewCondVar(rwlock->rw_lock); | 
|  | if (rwlock->rw_reader_waitq == NULL) { | 
|  | goto failed; | 
|  | } | 
|  | rwlock->rw_writer_waitq = PR_NewCondVar(rwlock->rw_lock); | 
|  | if (rwlock->rw_writer_waitq == NULL) { | 
|  | goto failed; | 
|  | } | 
|  | rwlock->rw_reader_cnt = 0; | 
|  | rwlock->rw_writer_cnt = 0; | 
|  | rwlock->rw_lock_cnt = 0; | 
|  | return rwlock; | 
|  |  | 
|  | failed: | 
|  | if (rwlock->rw_reader_waitq != NULL) { | 
|  | PR_DestroyCondVar(rwlock->rw_reader_waitq); | 
|  | } | 
|  | if (rwlock->rw_lock != NULL) { | 
|  | PR_DestroyLock(rwlock->rw_lock); | 
|  | } | 
|  | PR_Free(rwlock->rw_name); | 
|  | PR_DELETE(rwlock); | 
|  | return NULL; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | /* | 
|  | ** Destroy the given RWLock "lock". | 
|  | */ | 
|  | PR_IMPLEMENT(void) | 
|  | PR_DestroyRWLock(PRRWLock *rwlock) | 
|  | { | 
|  | #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) | 
|  | int err; | 
|  | err = RWLOCK_DESTROY(&rwlock->rw_lock); | 
|  | PR_ASSERT(err == 0); | 
|  | #else | 
|  | PR_ASSERT(rwlock->rw_reader_cnt == 0); | 
|  | PR_DestroyCondVar(rwlock->rw_reader_waitq); | 
|  | PR_DestroyCondVar(rwlock->rw_writer_waitq); | 
|  | PR_DestroyLock(rwlock->rw_lock); | 
|  | #endif | 
|  | if (rwlock->rw_name != NULL) | 
|  | PR_Free(rwlock->rw_name); | 
|  | PR_DELETE(rwlock); | 
|  | } | 
|  |  | 
|  | /* | 
|  | ** Read-lock the RWLock. | 
|  | */ | 
|  | PR_IMPLEMENT(void) | 
|  | PR_RWLock_Rlock(PRRWLock *rwlock) | 
|  | { | 
|  | #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) | 
|  | int err; | 
|  | #endif | 
|  |  | 
|  | #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG | 
|  | /* | 
|  | * assert that rank ordering is not violated; the rank of 'rwlock' should | 
|  | * be equal to or greater than the highest rank of all the locks held by | 
|  | * the thread. | 
|  | */ | 
|  | PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) || | 
|  | (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK())); | 
|  | #endif | 
|  |  | 
|  | #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) | 
|  | err = RWLOCK_RDLOCK(&rwlock->rw_lock); | 
|  | PR_ASSERT(err == 0); | 
|  | #else | 
|  | PR_Lock(rwlock->rw_lock); | 
|  | /* | 
|  | * wait if write-locked or if a writer is waiting; preference for writers | 
|  | */ | 
|  | while ((rwlock->rw_lock_cnt < 0) || | 
|  | (rwlock->rw_writer_cnt > 0)) { | 
|  | rwlock->rw_reader_cnt++; | 
|  | PR_WaitCondVar(rwlock->rw_reader_waitq, PR_INTERVAL_NO_TIMEOUT); | 
|  | rwlock->rw_reader_cnt--; | 
|  | } | 
|  | /* | 
|  | * Increment read-lock count | 
|  | */ | 
|  | rwlock->rw_lock_cnt++; | 
|  |  | 
|  | PR_Unlock(rwlock->rw_lock); | 
|  | #endif | 
|  |  | 
|  | #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG | 
|  | /* | 
|  | * update thread's lock rank | 
|  | */ | 
|  | if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) | 
|  | _PR_SET_THREAD_RWLOCK_RANK(rwlock); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | /* | 
|  | ** Write-lock the RWLock. | 
|  | */ | 
|  | PR_IMPLEMENT(void) | 
|  | PR_RWLock_Wlock(PRRWLock *rwlock) | 
|  | { | 
|  | #if defined(DEBUG) | 
|  | PRThread *me = PR_GetCurrentThread(); | 
|  | #endif | 
|  | #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) | 
|  | int err; | 
|  | #endif | 
|  |  | 
|  | #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG | 
|  | /* | 
|  | * assert that rank ordering is not violated; the rank of 'rwlock' should | 
|  | * be equal to or greater than the highest rank of all the locks held by | 
|  | * the thread. | 
|  | */ | 
|  | PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) || | 
|  | (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK())); | 
|  | #endif | 
|  |  | 
|  | #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) | 
|  | err = RWLOCK_WRLOCK(&rwlock->rw_lock); | 
|  | PR_ASSERT(err == 0); | 
|  | #else | 
|  | PR_Lock(rwlock->rw_lock); | 
|  | /* | 
|  | * wait if read locked | 
|  | */ | 
|  | while (rwlock->rw_lock_cnt != 0) { | 
|  | rwlock->rw_writer_cnt++; | 
|  | PR_WaitCondVar(rwlock->rw_writer_waitq, PR_INTERVAL_NO_TIMEOUT); | 
|  | rwlock->rw_writer_cnt--; | 
|  | } | 
|  | /* | 
|  | * apply write lock | 
|  | */ | 
|  | rwlock->rw_lock_cnt--; | 
|  | PR_ASSERT(rwlock->rw_lock_cnt == -1); | 
|  | #ifdef DEBUG | 
|  | PR_ASSERT(me != NULL); | 
|  | rwlock->rw_owner = me; | 
|  | #endif | 
|  | PR_Unlock(rwlock->rw_lock); | 
|  | #endif | 
|  |  | 
|  | #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG | 
|  | /* | 
|  | * update thread's lock rank | 
|  | */ | 
|  | if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) | 
|  | _PR_SET_THREAD_RWLOCK_RANK(rwlock); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | /* | 
|  | ** Unlock the RW lock. | 
|  | */ | 
|  | PR_IMPLEMENT(void) | 
|  | PR_RWLock_Unlock(PRRWLock *rwlock) | 
|  | { | 
|  | #if defined(DEBUG) | 
|  | PRThread *me = PR_GetCurrentThread(); | 
|  | #endif | 
|  | #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) | 
|  | int err; | 
|  | #endif | 
|  |  | 
|  | #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) | 
|  | err = RWLOCK_UNLOCK(&rwlock->rw_lock); | 
|  | PR_ASSERT(err == 0); | 
|  | #else | 
|  | PR_Lock(rwlock->rw_lock); | 
|  | /* | 
|  | * lock must be read or write-locked | 
|  | */ | 
|  | PR_ASSERT(rwlock->rw_lock_cnt != 0); | 
|  | if (rwlock->rw_lock_cnt > 0) { | 
|  |  | 
|  | /* | 
|  | * decrement read-lock count | 
|  | */ | 
|  | rwlock->rw_lock_cnt--; | 
|  | if (rwlock->rw_lock_cnt == 0) { | 
|  | /* | 
|  | * lock is not read-locked anymore; wakeup a waiting writer | 
|  | */ | 
|  | if (rwlock->rw_writer_cnt > 0) | 
|  | PR_NotifyCondVar(rwlock->rw_writer_waitq); | 
|  | } | 
|  | } else { | 
|  | PR_ASSERT(rwlock->rw_lock_cnt == -1); | 
|  |  | 
|  | rwlock->rw_lock_cnt = 0; | 
|  | #ifdef DEBUG | 
|  | PR_ASSERT(rwlock->rw_owner == me); | 
|  | rwlock->rw_owner = NULL; | 
|  | #endif | 
|  | /* | 
|  | * wakeup a writer, if present; preference for writers | 
|  | */ | 
|  | if (rwlock->rw_writer_cnt > 0) | 
|  | PR_NotifyCondVar(rwlock->rw_writer_waitq); | 
|  | /* | 
|  | * else, wakeup all readers, if any | 
|  | */ | 
|  | else if (rwlock->rw_reader_cnt > 0) | 
|  | PR_NotifyAllCondVar(rwlock->rw_reader_waitq); | 
|  | } | 
|  | PR_Unlock(rwlock->rw_lock); | 
|  | #endif | 
|  |  | 
|  | #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG | 
|  | /* | 
|  | * update thread's lock rank | 
|  | */ | 
|  | if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) | 
|  | _PR_UNSET_THREAD_RWLOCK_RANK(rwlock); | 
|  | #endif | 
|  | return; | 
|  | } | 
|  |  | 
|  | #ifndef _PR_RWLOCK_RANK_ORDER_DEBUG | 
|  |  | 
|  | void _PR_InitRWLocks(void) { } | 
|  |  | 
|  | #else | 
|  |  | 
|  | void _PR_InitRWLocks(void) | 
|  | { | 
|  | /* | 
|  | * allocated thread-private-data index for rwlock list | 
|  | */ | 
|  | if (PR_NewThreadPrivateIndex(&pr_thread_rwlock_key, | 
|  | _PR_RELEASE_LOCK_STACK) == PR_FAILURE) { | 
|  | pr_thread_rwlock_alloc_failed = 1; | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * _PR_SET_THREAD_RWLOCK_RANK | 
|  | *		Set a thread's lock rank, which is the highest of the ranks of all | 
|  | *		the locks held by the thread. Pointers to the locks are added to a | 
|  | *		per-thread list, which is anchored off a thread-private data key. | 
|  | */ | 
|  |  | 
|  | static void | 
|  | _PR_SET_THREAD_RWLOCK_RANK(PRRWLock *rwlock) | 
|  | { | 
|  | thread_rwlock_stack *lock_stack; | 
|  | PRStatus rv; | 
|  |  | 
|  | /* | 
|  | * allocate a lock stack | 
|  | */ | 
|  | if ((lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key)) == NULL) { | 
|  | lock_stack = (thread_rwlock_stack *) | 
|  | PR_CALLOC(1 * sizeof(thread_rwlock_stack)); | 
|  | if (lock_stack) { | 
|  | rv = PR_SetThreadPrivate(pr_thread_rwlock_key, lock_stack); | 
|  | if (rv == PR_FAILURE) { | 
|  | PR_DELETE(lock_stack); | 
|  | pr_thread_rwlock_alloc_failed = 1; | 
|  | return; | 
|  | } | 
|  | } else { | 
|  | pr_thread_rwlock_alloc_failed = 1; | 
|  | return; | 
|  | } | 
|  | } | 
|  | /* | 
|  | * add rwlock to lock stack, if limit is not exceeded | 
|  | */ | 
|  | if (lock_stack) { | 
|  | if (lock_stack->trs_index < _PR_RWLOCK_RANK_ORDER_LIMIT) | 
|  | lock_stack->trs_stack[lock_stack->trs_index++] = rwlock; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | _PR_RELEASE_LOCK_STACK(void *lock_stack) | 
|  | { | 
|  | PR_ASSERT(lock_stack); | 
|  | PR_DELETE(lock_stack); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * _PR_GET_THREAD_RWLOCK_RANK | 
|  | * | 
|  | *		return thread's lock rank. If thread-private-data for the lock | 
|  | *		stack is not allocated, return PR_RWLOCK_RANK_NONE. | 
|  | */ | 
|  |  | 
|  | static PRUint32 | 
|  | _PR_GET_THREAD_RWLOCK_RANK(void) | 
|  | { | 
|  | thread_rwlock_stack *lock_stack; | 
|  |  | 
|  | lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key); | 
|  | if (lock_stack == NULL || lock_stack->trs_index == 0) | 
|  | return (PR_RWLOCK_RANK_NONE); | 
|  | else | 
|  | return(lock_stack->trs_stack[lock_stack->trs_index - 1]->rw_rank); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * _PR_UNSET_THREAD_RWLOCK_RANK | 
|  | * | 
|  | *		remove the rwlock from the lock stack. Since locks may not be | 
|  | *		unlocked in a FIFO order, the entire lock stack is searched. | 
|  | */ | 
|  |  | 
|  | static void | 
|  | _PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock *rwlock) | 
|  | { | 
|  | thread_rwlock_stack *lock_stack; | 
|  | int new_index = 0, index, done = 0; | 
|  |  | 
|  | lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key); | 
|  |  | 
|  | PR_ASSERT(lock_stack != NULL); | 
|  |  | 
|  | for (index = lock_stack->trs_index - 1; index >= 0; index--) { | 
|  | if (!done && (lock_stack->trs_stack[index] == rwlock))  { | 
|  | /* | 
|  | * reset the slot for rwlock | 
|  | */ | 
|  | lock_stack->trs_stack[index] = NULL; | 
|  | done = 1; | 
|  | } | 
|  | /* | 
|  | * search for the lowest-numbered empty slot, above which there are | 
|  | * no non-empty slots | 
|  | */ | 
|  | if (!new_index && (lock_stack->trs_stack[index] != NULL)) | 
|  | new_index = index + 1; | 
|  | if (done && new_index) | 
|  | break; | 
|  | } | 
|  | /* | 
|  | * set top of stack to highest numbered empty slot | 
|  | */ | 
|  | lock_stack->trs_index = new_index; | 
|  |  | 
|  | } | 
|  |  | 
|  | #endif 	/* _PR_RWLOCK_RANK_ORDER_DEBUG */ |