nssrwlk.c
上传用户:lyxiangda
上传日期:2007-01-12
资源大小:3042k
文件大小:15k
- /*
- * The contents of this file are subject to the Mozilla Public
- * License Version 1.1 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * The Original Code is the Netscape security libraries.
- *
- * The Initial Developer of the Original Code is Netscape
- * Communications Corporation. Portions created by Netscape are
- * Copyright (C) 1994-2000 Netscape Communications Corporation. All
- * Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License Version 2 or later (the
- * "GPL"), in which case the provisions of the GPL are applicable
- * instead of those above. If you wish to allow use of your
- * version of this file only under the terms of the GPL and not to
- * allow others to use your version of this file under the MPL,
- * indicate your decision by deleting the provisions above and
- * replace them with the notice and other provisions required by
- * the GPL. If you do not delete the provisions above, a recipient
- * may use your version of this file under either the MPL or the
- * GPL.
- */
- #include "nssrwlk.h"
- #include "nspr.h"
- PR_BEGIN_EXTERN_C
- /*
- * Reader-writer lock
- */
- struct nssRWLockStr {
- PRLock * rw_lock;
- char * rw_name; /* lock name */
- PRUint32 rw_rank; /* rank of the lock */
- PRInt32 rw_writer_locks; /* == 0, if unlocked */
- PRInt32 rw_reader_locks; /* == 0, if unlocked */
- /* > 0 , # of read locks */
- PRUint32 rw_waiting_readers; /* number of waiting readers */
- PRUint32 rw_waiting_writers; /* number of waiting writers */
- PRCondVar * rw_reader_waitq; /* cvar for readers */
- PRCondVar * rw_writer_waitq; /* cvar for writers */
- PRThread * rw_owner; /* lock owner for write-lock */
- /* Non-null if write lock held. */
- };
- PR_END_EXTERN_C
- #include <string.h>
- #ifdef DEBUG_RANK_ORDER
- #define NSS_RWLOCK_RANK_ORDER_DEBUG /* enable deadlock detection using
- rank-order for locks
- */
- #endif
- #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
- static PRUintn nss_thread_rwlock_initialized;
- static PRUintn nss_thread_rwlock; /* TPD key for lock stack */
- static PRUintn nss_thread_rwlock_alloc_failed;
- #define _NSS_RWLOCK_RANK_ORDER_LIMIT 10
- typedef struct thread_rwlock_stack {
- PRInt32 trs_index; /* top of stack */
- NSSRWLock *trs_stack[_NSS_RWLOCK_RANK_ORDER_LIMIT]; /* stack of lock
- pointers */
- } thread_rwlock_stack;
- /* forward static declarations. */
- static PRUint32 nssRWLock_GetThreadRank(PRThread *me);
- static void nssRWLock_SetThreadRank(PRThread *me, NSSRWLock *rwlock);
- static void nssRWLock_UnsetThreadRank(PRThread *me, NSSRWLock *rwlock);
- static void nssRWLock_ReleaseLockStack(void *lock_stack);
- #endif
- #define UNTIL(x) while(!(x))
- /*
- * Reader/Writer Locks
- */
- /*
- * NSSRWLock_New
- * Create a reader-writer lock, with the given lock rank and lock name
- *
- */
- PR_IMPLEMENT(NSSRWLock *)
- NSSRWLock_New(PRUint32 lock_rank, const char *lock_name)
- {
- NSSRWLock *rwlock;
- rwlock = PR_NEWZAP(NSSRWLock);
- if (rwlock == NULL)
- return NULL;
- rwlock->rw_lock = PR_NewLock();
- if (rwlock->rw_lock == NULL) {
- goto loser;
- }
- rwlock->rw_reader_waitq = PR_NewCondVar(rwlock->rw_lock);
- if (rwlock->rw_reader_waitq == NULL) {
- goto loser;
- }
- rwlock->rw_writer_waitq = PR_NewCondVar(rwlock->rw_lock);
- if (rwlock->rw_writer_waitq == NULL) {
- goto loser;
- }
- if (lock_name != NULL) {
- rwlock->rw_name = (char*) PR_Malloc(strlen(lock_name) + 1);
- if (rwlock->rw_name == NULL) {
- goto loser;
- }
- strcpy(rwlock->rw_name, lock_name);
- } else {
- rwlock->rw_name = NULL;
- }
- rwlock->rw_rank = lock_rank;
- rwlock->rw_waiting_readers = 0;
- rwlock->rw_waiting_writers = 0;
- rwlock->rw_reader_locks = 0;
- rwlock->rw_writer_locks = 0;
- return rwlock;
- loser:
- NSSRWLock_Destroy(rwlock);
- return(NULL);
- }
- /*
- ** Destroy the given RWLock "lock".
- */
- PR_IMPLEMENT(void)
- NSSRWLock_Destroy(NSSRWLock *rwlock)
- {
- PR_ASSERT(rwlock != NULL);
- PR_ASSERT(rwlock->rw_waiting_readers == 0);
- /* XXX Shouldn't we lock the PRLock before destroying this?? */
- if (rwlock->rw_name)
- PR_Free(rwlock->rw_name);
- if (rwlock->rw_reader_waitq)
- PR_DestroyCondVar(rwlock->rw_reader_waitq);
- if (rwlock->rw_writer_waitq)
- PR_DestroyCondVar(rwlock->rw_writer_waitq);
- if (rwlock->rw_lock)
- PR_DestroyLock(rwlock->rw_lock);
- PR_DELETE(rwlock);
- }
- /***********************************************************************
- ** Given the address of a NULL pointer to a NSSRWLock,
- ** atomically initializes that pointer to a newly created NSSRWLock.
- ** Returns the value placed into that pointer, or NULL.
- ** If the lock cannot be created because of resource constraints,
- ** the pointer will be left NULL.
- **
- ***********************************************************************/
- PR_IMPLEMENT(NSSRWLock *)
- nssRWLock_AtomicCreate( NSSRWLock ** prwlock,
- PRUint32 lock_rank,
- const char * lock_name)
- {
- NSSRWLock * rwlock;
- static PRInt32 initializers;
- PR_ASSERT(prwlock != NULL);
- /* atomically initialize the lock */
- while (NULL == (rwlock = *prwlock)) {
- PRInt32 myAttempt = PR_AtomicIncrement(&initializers);
- if (myAttempt == 1) {
- *prwlock = rwlock = NSSRWLock_New(lock_rank, lock_name);
- (void) PR_AtomicDecrement(&initializers);
- break;
- }
- PR_Sleep(PR_INTERVAL_NO_WAIT); /* PR_Yield() */
- (void) PR_AtomicDecrement(&initializers);
- }
- return rwlock;
- }
- /*
- ** Read-lock the RWLock.
- */
- PR_IMPLEMENT(void)
- NSSRWLock_LockRead(NSSRWLock *rwlock)
- {
- PRThread *me = PR_GetCurrentThread();
- PR_Lock(rwlock->rw_lock);
- #ifdef NSS_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 == NSS_RWLOCK_RANK_NONE) ||
- (rwlock->rw_rank >= nssRWLock_GetThreadRank(me)));
- #endif
- /*
- * wait if write-locked or if a writer is waiting; preference for writers
- */
- UNTIL ( (rwlock->rw_owner == me) || /* I own it, or */
- ((rwlock->rw_owner == NULL) && /* no-one owns it, and */
- (rwlock->rw_waiting_writers == 0))) { /* no-one is waiting to own */
- rwlock->rw_waiting_readers++;
- PR_WaitCondVar(rwlock->rw_reader_waitq, PR_INTERVAL_NO_TIMEOUT);
- rwlock->rw_waiting_readers--;
- }
- rwlock->rw_reader_locks++; /* Increment read-lock count */
- PR_Unlock(rwlock->rw_lock);
- #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
- nssRWLock_SetThreadRank(me, rwlock);/* update thread's lock rank */
- #endif
- }
- /* Unlock a Read lock held on this RW lock.
- */
- PR_IMPLEMENT(void)
- NSSRWLock_UnlockRead(NSSRWLock *rwlock)
- {
- PRThread *me = PR_GetCurrentThread();
- PR_Lock(rwlock->rw_lock);
- PR_ASSERT(rwlock->rw_reader_locks > 0); /* lock must be read locked */
- if (( rwlock->rw_reader_locks > 0) && /* caller isn't screwey */
- (--rwlock->rw_reader_locks == 0) && /* not read locked any more */
- ( rwlock->rw_owner == NULL) && /* not write locked */
- ( rwlock->rw_waiting_writers > 0)) { /* someone's waiting. */
- PR_NotifyCondVar(rwlock->rw_writer_waitq); /* wake him up. */
- }
- PR_Unlock(rwlock->rw_lock);
- #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
- /*
- * update thread's lock rank
- */
- nssRWLock_UnsetThreadRank(me, rwlock);
- #endif
- return;
- }
- /*
- ** Write-lock the RWLock.
- */
- PR_IMPLEMENT(void)
- NSSRWLock_LockWrite(NSSRWLock *rwlock)
- {
- PRInt32 lock_acquired = 0;
- PRThread *me = PR_GetCurrentThread();
- PR_Lock(rwlock->rw_lock);
- #ifdef NSS_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 == NSS_RWLOCK_RANK_NONE) ||
- (rwlock->rw_rank >= nssRWLock_GetThreadRank(me)));
- #endif
- /*
- * wait if read locked or write locked.
- */
- PR_ASSERT(rwlock->rw_reader_locks >= 0);
- PR_ASSERT(me != NULL);
- UNTIL ( (rwlock->rw_owner == me) || /* I own write lock, or */
- ((rwlock->rw_owner == NULL) && /* no writer and */
- (rwlock->rw_reader_locks == 0))) { /* no readers, either. */
- rwlock->rw_waiting_writers++;
- PR_WaitCondVar(rwlock->rw_writer_waitq, PR_INTERVAL_NO_TIMEOUT);
- rwlock->rw_waiting_writers--;
- PR_ASSERT(rwlock->rw_reader_locks >= 0);
- }
- PR_ASSERT(rwlock->rw_reader_locks == 0);
- /*
- * apply write lock
- */
- rwlock->rw_owner = me;
- rwlock->rw_writer_locks++; /* Increment write-lock count */
- PR_Unlock(rwlock->rw_lock);
- #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
- /*
- * update thread's lock rank
- */
- nssRWLock_SetThreadRank(me,rwlock);
- #endif
- }
- /* Unlock a Read lock held on this RW lock.
- */
- PR_IMPLEMENT(void)
- NSSRWLock_UnlockWrite(NSSRWLock *rwlock)
- {
- PRThread *me = PR_GetCurrentThread();
- PR_Lock(rwlock->rw_lock);
- PR_ASSERT(rwlock->rw_owner == me); /* lock must be write-locked by me. */
- PR_ASSERT(rwlock->rw_writer_locks > 0); /* lock must be write locked */
- if ( rwlock->rw_owner == me && /* I own it, and */
- rwlock->rw_writer_locks > 0 && /* I own it, and */
- --rwlock->rw_writer_locks == 0) { /* I'm all done with it */
- rwlock->rw_owner = NULL; /* I don't own it any more. */
- if (rwlock->rw_reader_locks == 0) { /* no readers, wake up somebody. */
- /* Give preference to waiting writers. */
- if (rwlock->rw_waiting_writers > 0)
- PR_NotifyCondVar(rwlock->rw_writer_waitq);
- else if (rwlock->rw_waiting_readers > 0)
- PR_NotifyAllCondVar(rwlock->rw_reader_waitq);
- }
- }
- PR_Unlock(rwlock->rw_lock);
- #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
- /*
- * update thread's lock rank
- */
- nssRWLock_UnsetThreadRank(me, rwlock);
- #endif
- return;
- }
- /* This is primarily for debugging, i.e. for inclusion in ASSERT calls. */
- PR_IMPLEMENT(PRBool)
- NSSRWLock_HaveWriteLock(NSSRWLock *rwlock) {
- PRBool ownWriteLock;
- PRThread *me = PR_GetCurrentThread();
- /* This lock call isn't really necessary.
- ** If this thread is the owner, that fact cannot change during this call,
- ** because this thread is in this call.
- ** If this thread is NOT the owner, the owner could change, but it
- ** could not become this thread.
- */
- #if UNNECESSARY
- PR_Lock(rwlock->rw_lock);
- #endif
- ownWriteLock = (PRBool)(me == rwlock->rw_owner);
- #if UNNECESSARY
- PR_Unlock(rwlock->rw_lock);
- #endif
- return ownWriteLock;
- }
- #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
- /*
- * nssRWLock_SetThreadRank
- * 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
- nssRWLock_SetThreadRank(PRThread *me, NSSRWLock *rwlock)
- {
- thread_rwlock_stack *lock_stack;
- PRStatus rv;
- /*
- * allocated thread-private-data for rwlock list, if not already allocated
- */
- if (!nss_thread_rwlock_initialized) {
- /*
- * allocate tpd, only if not failed already
- */
- if (!nss_thread_rwlock_alloc_failed) {
- if (PR_NewThreadPrivateIndex(&nss_thread_rwlock,
- nssRWLock_ReleaseLockStack)
- == PR_FAILURE) {
- nss_thread_rwlock_alloc_failed = 1;
- return;
- }
- } else
- return;
- }
- /*
- * allocate a lock stack
- */
- if ((lock_stack = PR_GetThreadPrivate(nss_thread_rwlock)) == NULL) {
- lock_stack = (thread_rwlock_stack *)
- PR_CALLOC(1 * sizeof(thread_rwlock_stack));
- if (lock_stack) {
- rv = PR_SetThreadPrivate(nss_thread_rwlock, lock_stack);
- if (rv == PR_FAILURE) {
- PR_DELETE(lock_stack);
- nss_thread_rwlock_alloc_failed = 1;
- return;
- }
- } else {
- nss_thread_rwlock_alloc_failed = 1;
- return;
- }
- }
- /*
- * add rwlock to lock stack, if limit is not exceeded
- */
- if (lock_stack) {
- if (lock_stack->trs_index < _NSS_RWLOCK_RANK_ORDER_LIMIT)
- lock_stack->trs_stack[lock_stack->trs_index++] = rwlock;
- }
- nss_thread_rwlock_initialized = 1;
- }
- static void
- nssRWLock_ReleaseLockStack(void *lock_stack)
- {
- PR_ASSERT(lock_stack);
- PR_DELETE(lock_stack);
- }
- /*
- * nssRWLock_GetThreadRank
- *
- * return thread's lock rank. If thread-private-data for the lock
- * stack is not allocated, return NSS_RWLOCK_RANK_NONE.
- */
- static PRUint32
- nssRWLock_GetThreadRank(PRThread *me)
- {
- thread_rwlock_stack *lock_stack;
- if (nss_thread_rwlock_initialized) {
- if ((lock_stack = PR_GetThreadPrivate(nss_thread_rwlock)) == NULL)
- return (NSS_RWLOCK_RANK_NONE);
- else
- return(lock_stack->trs_stack[lock_stack->trs_index - 1]->rw_rank);
- } else
- return (NSS_RWLOCK_RANK_NONE);
- }
- /*
- * nssRWLock_UnsetThreadRank
- *
- * 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
- nssRWLock_UnsetThreadRank(PRThread *me, NSSRWLock *rwlock)
- {
- thread_rwlock_stack *lock_stack;
- int new_index = 0, index, done = 0;
- if (!nss_thread_rwlock_initialized)
- return;
- lock_stack = PR_GetThreadPrivate(nss_thread_rwlock);
- PR_ASSERT(lock_stack != NULL);
- index = lock_stack->trs_index - 1;
- while (index-- >= 0) {
- if ((lock_stack->trs_stack[index] == rwlock) && !done) {
- /*
- * 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 ((lock_stack->trs_stack[index] != NULL) && !new_index)
- 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 /* NSS_RWLOCK_RANK_ORDER_DEBUG */