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