nssrwlk.c
上传用户:lyxiangda
上传日期:2007-01-12
资源大小:3042k
文件大小:15k
源码类别:

CA认证

开发平台:

WINDOWS

  1. /*
  2.  * The contents of this file are subject to the Mozilla Public
  3.  * License Version 1.1 (the "License"); you may not use this file
  4.  * except in compliance with the License. You may obtain a copy of
  5.  * the License at http://www.mozilla.org/MPL/
  6.  * 
  7.  * Software distributed under the License is distributed on an "AS
  8.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  9.  * implied. See the License for the specific language governing
  10.  * rights and limitations under the License.
  11.  * 
  12.  * The Original Code is the Netscape security libraries.
  13.  * 
  14.  * The Initial Developer of the Original Code is Netscape
  15.  * Communications Corporation.  Portions created by Netscape are 
  16.  * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
  17.  * Rights Reserved.
  18.  * 
  19.  * Contributor(s):
  20.  * 
  21.  * Alternatively, the contents of this file may be used under the
  22.  * terms of the GNU General Public License Version 2 or later (the
  23.  * "GPL"), in which case the provisions of the GPL are applicable 
  24.  * instead of those above.  If you wish to allow use of your 
  25.  * version of this file only under the terms of the GPL and not to
  26.  * allow others to use your version of this file under the MPL,
  27.  * indicate your decision by deleting the provisions above and
  28.  * replace them with the notice and other provisions required by
  29.  * the GPL.  If you do not delete the provisions above, a recipient
  30.  * may use your version of this file under either the MPL or the
  31.  * GPL.
  32.  */
  33. #include "nssrwlk.h"
  34. #include "nspr.h"
  35. PR_BEGIN_EXTERN_C
  36. /*
  37.  * Reader-writer lock
  38.  */
  39. struct nssRWLockStr {
  40.     PRLock *        rw_lock;
  41.     char   *        rw_name;            /* lock name                    */
  42.     PRUint32        rw_rank;            /* rank of the lock             */
  43.     PRInt32         rw_writer_locks;    /* ==  0, if unlocked           */
  44.     PRInt32         rw_reader_locks;    /* ==  0, if unlocked           */
  45.                                         /* > 0  , # of read locks       */
  46.     PRUint32        rw_waiting_readers; /* number of waiting readers    */
  47.     PRUint32        rw_waiting_writers; /* number of waiting writers    */
  48.     PRCondVar *     rw_reader_waitq;    /* cvar for readers             */
  49.     PRCondVar *     rw_writer_waitq;    /* cvar for writers             */
  50.     PRThread  *     rw_owner;           /* lock owner for write-lock    */
  51.                                         /* Non-null if write lock held. */
  52. };
  53. PR_END_EXTERN_C
  54. #include <string.h>
  55. #ifdef DEBUG_RANK_ORDER
  56. #define NSS_RWLOCK_RANK_ORDER_DEBUG /* enable deadlock detection using
  57.                                        rank-order for locks
  58.                                     */
  59. #endif
  60. #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
  61. static PRUintn  nss_thread_rwlock_initialized;
  62. static PRUintn  nss_thread_rwlock;               /* TPD key for lock stack */
  63. static PRUintn  nss_thread_rwlock_alloc_failed;
  64. #define _NSS_RWLOCK_RANK_ORDER_LIMIT 10
  65. typedef struct thread_rwlock_stack {
  66.     PRInt32     trs_index;                                  /* top of stack */
  67.     NSSRWLock    *trs_stack[_NSS_RWLOCK_RANK_ORDER_LIMIT];  /* stack of lock
  68.                                                                pointers */
  69. } thread_rwlock_stack;
  70. /* forward static declarations. */
  71. static PRUint32 nssRWLock_GetThreadRank(PRThread *me);
  72. static void     nssRWLock_SetThreadRank(PRThread *me, NSSRWLock *rwlock);
  73. static void     nssRWLock_UnsetThreadRank(PRThread *me, NSSRWLock *rwlock);
  74. static void     nssRWLock_ReleaseLockStack(void *lock_stack);
  75. #endif
  76. #define UNTIL(x) while(!(x))
  77. /*
  78.  * Reader/Writer Locks
  79.  */
  80. /*
  81.  * NSSRWLock_New
  82.  *      Create a reader-writer lock, with the given lock rank and lock name
  83.  *
  84.  */
  85. PR_IMPLEMENT(NSSRWLock *)
  86. NSSRWLock_New(PRUint32 lock_rank, const char *lock_name)
  87. {
  88.     NSSRWLock *rwlock;
  89.     rwlock = PR_NEWZAP(NSSRWLock);
  90.     if (rwlock == NULL)
  91.         return NULL;
  92.     rwlock->rw_lock = PR_NewLock();
  93.     if (rwlock->rw_lock == NULL) {
  94. goto loser;
  95.     }
  96.     rwlock->rw_reader_waitq = PR_NewCondVar(rwlock->rw_lock);
  97.     if (rwlock->rw_reader_waitq == NULL) {
  98. goto loser;
  99.     }
  100.     rwlock->rw_writer_waitq = PR_NewCondVar(rwlock->rw_lock);
  101.     if (rwlock->rw_writer_waitq == NULL) {
  102. goto loser;
  103.     }
  104.     if (lock_name != NULL) {
  105.         rwlock->rw_name = (char*) PR_Malloc(strlen(lock_name) + 1);
  106.         if (rwlock->rw_name == NULL) {
  107.     goto loser;
  108.         }
  109.         strcpy(rwlock->rw_name, lock_name);
  110.     } else {
  111.         rwlock->rw_name = NULL;
  112.     }
  113.     rwlock->rw_rank            = lock_rank;
  114.     rwlock->rw_waiting_readers = 0;
  115.     rwlock->rw_waiting_writers = 0;
  116.     rwlock->rw_reader_locks    = 0;
  117.     rwlock->rw_writer_locks    = 0;
  118.     return rwlock;
  119. loser:
  120.     NSSRWLock_Destroy(rwlock);
  121.     return(NULL);
  122. }
  123. /*
  124. ** Destroy the given RWLock "lock".
  125. */
  126. PR_IMPLEMENT(void)
  127. NSSRWLock_Destroy(NSSRWLock *rwlock)
  128. {
  129.     PR_ASSERT(rwlock != NULL);
  130.     PR_ASSERT(rwlock->rw_waiting_readers == 0);
  131.     /* XXX Shouldn't we lock the PRLock before destroying this?? */
  132.     if (rwlock->rw_name)
  133.      PR_Free(rwlock->rw_name);
  134.     if (rwlock->rw_reader_waitq)
  135.      PR_DestroyCondVar(rwlock->rw_reader_waitq);
  136.     if (rwlock->rw_writer_waitq)
  137. PR_DestroyCondVar(rwlock->rw_writer_waitq);
  138.     if (rwlock->rw_lock)
  139. PR_DestroyLock(rwlock->rw_lock);
  140.     PR_DELETE(rwlock);
  141. }
  142. /***********************************************************************
  143. **  Given the address of a NULL pointer to a NSSRWLock, 
  144. **  atomically initializes that pointer to a newly created NSSRWLock.
  145. **  Returns the value placed into that pointer, or NULL.
  146. **   If the lock cannot be created because of resource constraints, 
  147. **   the pointer will be left NULL.
  148. **  
  149. ***********************************************************************/
  150. PR_IMPLEMENT(NSSRWLock *)
  151. nssRWLock_AtomicCreate( NSSRWLock  ** prwlock, 
  152. PRUint32      lock_rank, 
  153. const char *  lock_name)
  154. {
  155.     NSSRWLock  *    rwlock;
  156.     static PRInt32  initializers;
  157.     PR_ASSERT(prwlock != NULL);
  158.     /* atomically initialize the lock */
  159.     while (NULL == (rwlock = *prwlock)) {
  160.         PRInt32 myAttempt = PR_AtomicIncrement(&initializers);
  161.         if (myAttempt == 1) {
  162.     *prwlock = rwlock = NSSRWLock_New(lock_rank, lock_name);
  163.             (void) PR_AtomicDecrement(&initializers);
  164.             break;
  165.         }
  166.         PR_Sleep(PR_INTERVAL_NO_WAIT);          /* PR_Yield() */
  167.         (void) PR_AtomicDecrement(&initializers);
  168.     }
  169.     return rwlock;
  170. }
  171. /*
  172. ** Read-lock the RWLock.
  173. */
  174. PR_IMPLEMENT(void)
  175. NSSRWLock_LockRead(NSSRWLock *rwlock)
  176. {
  177.     PRThread *me = PR_GetCurrentThread();
  178.     PR_Lock(rwlock->rw_lock);
  179. #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
  180.     /*
  181.      * assert that rank ordering is not violated; the rank of 'rwlock' should
  182.      * be equal to or greater than the highest rank of all the locks held by
  183.      * the thread.
  184.      */
  185.     PR_ASSERT((rwlock->rw_rank == NSS_RWLOCK_RANK_NONE) ||
  186.               (rwlock->rw_rank >= nssRWLock_GetThreadRank(me)));
  187. #endif
  188.     /*
  189.      * wait if write-locked or if a writer is waiting; preference for writers
  190.      */
  191.     UNTIL ( (rwlock->rw_owner == me) ||   /* I own it, or        */
  192.    ((rwlock->rw_owner == NULL) &&   /* no-one owns it, and */
  193.     (rwlock->rw_waiting_writers == 0))) { /* no-one is waiting to own */
  194. rwlock->rw_waiting_readers++;
  195. PR_WaitCondVar(rwlock->rw_reader_waitq, PR_INTERVAL_NO_TIMEOUT);
  196. rwlock->rw_waiting_readers--;
  197.     }
  198.     rwlock->rw_reader_locks++;  /* Increment read-lock count */
  199.     PR_Unlock(rwlock->rw_lock);
  200. #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
  201.     nssRWLock_SetThreadRank(me, rwlock);/* update thread's lock rank */
  202. #endif
  203. }
  204. /* Unlock a Read lock held on this RW lock.
  205. */
  206. PR_IMPLEMENT(void)
  207. NSSRWLock_UnlockRead(NSSRWLock *rwlock)
  208. {
  209.     PRThread *me = PR_GetCurrentThread();
  210.     PR_Lock(rwlock->rw_lock);
  211.     PR_ASSERT(rwlock->rw_reader_locks > 0); /* lock must be read locked */
  212.     if ((  rwlock->rw_reader_locks  > 0)  && /* caller isn't screwey */
  213.         (--rwlock->rw_reader_locks == 0)  && /* not read locked any more */
  214. (  rwlock->rw_owner        == NULL) && /* not write locked */
  215. (  rwlock->rw_waiting_writers > 0)) { /* someone's waiting. */
  216. PR_NotifyCondVar(rwlock->rw_writer_waitq); /* wake him up. */
  217.     }
  218.     PR_Unlock(rwlock->rw_lock);
  219. #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
  220.     /*
  221.      * update thread's lock rank
  222.      */
  223.     nssRWLock_UnsetThreadRank(me, rwlock);
  224. #endif
  225.     return;
  226. }
  227. /*
  228. ** Write-lock the RWLock.
  229. */
  230. PR_IMPLEMENT(void)
  231. NSSRWLock_LockWrite(NSSRWLock *rwlock)
  232. {
  233.     PRInt32 lock_acquired = 0;
  234.     PRThread *me = PR_GetCurrentThread();
  235.     PR_Lock(rwlock->rw_lock);
  236. #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
  237.     /*
  238.      * assert that rank ordering is not violated; the rank of 'rwlock' should
  239.      * be equal to or greater than the highest rank of all the locks held by
  240.      * the thread.
  241.      */
  242.     PR_ASSERT((rwlock->rw_rank == NSS_RWLOCK_RANK_NONE) ||
  243.                     (rwlock->rw_rank >= nssRWLock_GetThreadRank(me)));
  244. #endif
  245.     /*
  246.      * wait if read locked or write locked.
  247.      */
  248.     PR_ASSERT(rwlock->rw_reader_locks >= 0);
  249.     PR_ASSERT(me != NULL);
  250.     UNTIL ( (rwlock->rw_owner == me) ||           /* I own write lock, or */
  251.    ((rwlock->rw_owner == NULL) &&   /* no writer   and */
  252.     (rwlock->rw_reader_locks == 0))) {    /* no readers, either. */
  253.         rwlock->rw_waiting_writers++;
  254.         PR_WaitCondVar(rwlock->rw_writer_waitq, PR_INTERVAL_NO_TIMEOUT);
  255.         rwlock->rw_waiting_writers--;
  256. PR_ASSERT(rwlock->rw_reader_locks >= 0);
  257.     }
  258.     PR_ASSERT(rwlock->rw_reader_locks == 0);
  259.     /*
  260.      * apply write lock
  261.      */
  262.     rwlock->rw_owner = me;
  263.     rwlock->rw_writer_locks++;  /* Increment write-lock count */
  264.     PR_Unlock(rwlock->rw_lock);
  265. #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
  266.     /*
  267.      * update thread's lock rank
  268.      */
  269.     nssRWLock_SetThreadRank(me,rwlock);
  270. #endif
  271. }
  272. /* Unlock a Read lock held on this RW lock.
  273. */
  274. PR_IMPLEMENT(void)
  275. NSSRWLock_UnlockWrite(NSSRWLock *rwlock)
  276. {
  277.     PRThread *me = PR_GetCurrentThread();
  278.     PR_Lock(rwlock->rw_lock);
  279.     PR_ASSERT(rwlock->rw_owner == me); /* lock must be write-locked by me.  */
  280.     PR_ASSERT(rwlock->rw_writer_locks > 0); /* lock must be write locked */
  281.     if (  rwlock->rw_owner        == me  && /* I own it, and            */
  282.           rwlock->rw_writer_locks  > 0   && /* I own it, and            */
  283.         --rwlock->rw_writer_locks == 0) { /* I'm all done with it     */
  284. rwlock->rw_owner = NULL; /* I don't own it any more. */
  285. if (rwlock->rw_reader_locks == 0) { /* no readers, wake up somebody. */
  286.     /* Give preference to waiting writers. */
  287.     if (rwlock->rw_waiting_writers > 0) 
  288. PR_NotifyCondVar(rwlock->rw_writer_waitq);
  289.     else if (rwlock->rw_waiting_readers > 0)
  290. PR_NotifyAllCondVar(rwlock->rw_reader_waitq);
  291. }
  292.     }
  293.     PR_Unlock(rwlock->rw_lock);
  294. #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
  295.     /*
  296.      * update thread's lock rank
  297.      */
  298.     nssRWLock_UnsetThreadRank(me, rwlock);
  299. #endif
  300.     return;
  301. }
  302. /* This is primarily for debugging, i.e. for inclusion in ASSERT calls. */
  303. PR_IMPLEMENT(PRBool)
  304. NSSRWLock_HaveWriteLock(NSSRWLock *rwlock) {
  305.     PRBool ownWriteLock;
  306.     PRThread *me = PR_GetCurrentThread();
  307.     /* This lock call isn't really necessary.
  308.     ** If this thread is the owner, that fact cannot change during this call,
  309.     ** because this thread is in this call.
  310.     ** If this thread is NOT the owner, the owner could change, but it 
  311.     ** could not become this thread.  
  312.     */
  313. #if UNNECESSARY
  314.     PR_Lock(rwlock->rw_lock);
  315. #endif
  316.     ownWriteLock = (PRBool)(me == rwlock->rw_owner);
  317. #if UNNECESSARY
  318.     PR_Unlock(rwlock->rw_lock);
  319. #endif
  320.     return ownWriteLock;
  321. }
  322. #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
  323. /*
  324.  * nssRWLock_SetThreadRank
  325.  *      Set a thread's lock rank, which is the highest of the ranks of all
  326.  *      the locks held by the thread. Pointers to the locks are added to a
  327.  *      per-thread list, which is anchored off a thread-private data key.
  328.  */
  329. static void
  330. nssRWLock_SetThreadRank(PRThread *me, NSSRWLock *rwlock)
  331. {
  332.     thread_rwlock_stack *lock_stack;
  333.     PRStatus rv;
  334.     /*
  335.      * allocated thread-private-data for rwlock list, if not already allocated
  336.      */
  337.     if (!nss_thread_rwlock_initialized) {
  338.         /*
  339.          * allocate tpd, only if not failed already
  340.          */
  341.         if (!nss_thread_rwlock_alloc_failed) {
  342.             if (PR_NewThreadPrivateIndex(&nss_thread_rwlock,
  343.                                         nssRWLock_ReleaseLockStack)
  344.                                                 == PR_FAILURE) {
  345.                 nss_thread_rwlock_alloc_failed = 1;
  346.                 return;
  347.             }
  348.         } else
  349.             return;
  350.     }
  351.     /*
  352.      * allocate a lock stack
  353.      */
  354.     if ((lock_stack = PR_GetThreadPrivate(nss_thread_rwlock)) == NULL) {
  355.         lock_stack = (thread_rwlock_stack *)
  356.                         PR_CALLOC(1 * sizeof(thread_rwlock_stack));
  357.         if (lock_stack) {
  358.             rv = PR_SetThreadPrivate(nss_thread_rwlock, lock_stack);
  359.             if (rv == PR_FAILURE) {
  360.                 PR_DELETE(lock_stack);
  361.                 nss_thread_rwlock_alloc_failed = 1;
  362.                 return;
  363.             }
  364.         } else {
  365.             nss_thread_rwlock_alloc_failed = 1;
  366.             return;
  367.         }
  368.     }
  369.     /*
  370.      * add rwlock to lock stack, if limit is not exceeded
  371.      */
  372.     if (lock_stack) {
  373.         if (lock_stack->trs_index < _NSS_RWLOCK_RANK_ORDER_LIMIT)
  374.             lock_stack->trs_stack[lock_stack->trs_index++] = rwlock;
  375.     }
  376.     nss_thread_rwlock_initialized = 1;
  377. }
  378. static void
  379. nssRWLock_ReleaseLockStack(void *lock_stack)
  380. {
  381.     PR_ASSERT(lock_stack);
  382.     PR_DELETE(lock_stack);
  383. }
  384. /*
  385.  * nssRWLock_GetThreadRank
  386.  *
  387.  *      return thread's lock rank. If thread-private-data for the lock
  388.  *      stack is not allocated, return NSS_RWLOCK_RANK_NONE.
  389.  */
  390. static PRUint32
  391. nssRWLock_GetThreadRank(PRThread *me)
  392. {
  393.     thread_rwlock_stack *lock_stack;
  394.     if (nss_thread_rwlock_initialized) {
  395.         if ((lock_stack = PR_GetThreadPrivate(nss_thread_rwlock)) == NULL)
  396.             return (NSS_RWLOCK_RANK_NONE);
  397.         else
  398.             return(lock_stack->trs_stack[lock_stack->trs_index - 1]->rw_rank);
  399.     } else
  400.             return (NSS_RWLOCK_RANK_NONE);
  401. }
  402. /*
  403.  * nssRWLock_UnsetThreadRank
  404.  *
  405.  *      remove the rwlock from the lock stack. Since locks may not be
  406.  *      unlocked in a FIFO order, the entire lock stack is searched.
  407.  */
  408. static void
  409. nssRWLock_UnsetThreadRank(PRThread *me, NSSRWLock *rwlock)
  410. {
  411.     thread_rwlock_stack *lock_stack;
  412.     int new_index = 0, index, done = 0;
  413.     if (!nss_thread_rwlock_initialized)
  414.         return;
  415.     lock_stack = PR_GetThreadPrivate(nss_thread_rwlock);
  416.     PR_ASSERT(lock_stack != NULL);
  417.     index = lock_stack->trs_index - 1;
  418.     while (index-- >= 0) {
  419.         if ((lock_stack->trs_stack[index] == rwlock) && !done)  {
  420.             /*
  421.              * reset the slot for rwlock
  422.              */
  423.             lock_stack->trs_stack[index] = NULL;
  424.             done = 1;
  425.         }
  426.         /*
  427.          * search for the lowest-numbered empty slot, above which there are
  428.          * no non-empty slots
  429.          */
  430.         if ((lock_stack->trs_stack[index] != NULL) && !new_index)
  431.             new_index = index + 1;
  432.         if (done && new_index)
  433.             break;
  434.     }
  435.     /*
  436.      * set top of stack to highest numbered empty slot
  437.      */
  438.     lock_stack->trs_index = new_index;
  439. }
  440. #endif  /* NSS_RWLOCK_RANK_ORDER_DEBUG */