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