security/nss/lib/util/nssrwlk.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include "nssrwlk.h"
     6 #include "nspr.h"
     8 PR_BEGIN_EXTERN_C
    10 /*
    11  * Reader-writer lock
    12  */
    13 struct nssRWLockStr {
    14     PZLock *        rw_lock;
    15     char   *        rw_name;            /* lock name                    */
    16     PRUint32        rw_rank;            /* rank of the lock             */
    17     PRInt32         rw_writer_locks;    /* ==  0, if unlocked           */
    18     PRInt32         rw_reader_locks;    /* ==  0, if unlocked           */
    19                                         /* > 0  , # of read locks       */
    20     PRUint32        rw_waiting_readers; /* number of waiting readers    */
    21     PRUint32        rw_waiting_writers; /* number of waiting writers    */
    22     PZCondVar *     rw_reader_waitq;    /* cvar for readers             */
    23     PZCondVar *     rw_writer_waitq;    /* cvar for writers             */
    24     PRThread  *     rw_owner;           /* lock owner for write-lock    */
    25                                         /* Non-null if write lock held. */
    26 };
    28 PR_END_EXTERN_C
    30 #include <string.h>
    32 #ifdef DEBUG_RANK_ORDER
    33 #define NSS_RWLOCK_RANK_ORDER_DEBUG /* enable deadlock detection using
    34                                        rank-order for locks
    35                                     */
    36 #endif
    38 #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
    40 static PRUintn  nss_thread_rwlock_initialized;
    41 static PRUintn  nss_thread_rwlock;               /* TPD key for lock stack */
    42 static PRUintn  nss_thread_rwlock_alloc_failed;
    44 #define _NSS_RWLOCK_RANK_ORDER_LIMIT 10
    46 typedef struct thread_rwlock_stack {
    47     PRInt32     trs_index;                                  /* top of stack */
    48     NSSRWLock    *trs_stack[_NSS_RWLOCK_RANK_ORDER_LIMIT];  /* stack of lock
    49                                                                pointers */
    50 } thread_rwlock_stack;
    52 /* forward static declarations. */
    53 static PRUint32 nssRWLock_GetThreadRank(PRThread *me);
    54 static void     nssRWLock_SetThreadRank(PRThread *me, NSSRWLock *rwlock);
    55 static void     nssRWLock_UnsetThreadRank(PRThread *me, NSSRWLock *rwlock);
    56 static void     nssRWLock_ReleaseLockStack(void *lock_stack);
    58 #endif
    60 #define UNTIL(x) while(!(x))
    62 /*
    63  * Reader/Writer Locks
    64  */
    66 /*
    67  * NSSRWLock_New
    68  *      Create a reader-writer lock, with the given lock rank and lock name
    69  *
    70  */
    72 NSSRWLock *
    73 NSSRWLock_New(PRUint32 lock_rank, const char *lock_name)
    74 {
    75     NSSRWLock *rwlock;
    77     rwlock = PR_NEWZAP(NSSRWLock);
    78     if (rwlock == NULL)
    79         return NULL;
    81     rwlock->rw_lock = PZ_NewLock(nssILockRWLock);
    82     if (rwlock->rw_lock == NULL) {
    83 	goto loser;
    84     }
    85     rwlock->rw_reader_waitq = PZ_NewCondVar(rwlock->rw_lock);
    86     if (rwlock->rw_reader_waitq == NULL) {
    87 	goto loser;
    88     }
    89     rwlock->rw_writer_waitq = PZ_NewCondVar(rwlock->rw_lock);
    90     if (rwlock->rw_writer_waitq == NULL) {
    91 	goto loser;
    92     }
    93     if (lock_name != NULL) {
    94         rwlock->rw_name = (char*) PR_Malloc(strlen(lock_name) + 1);
    95         if (rwlock->rw_name == NULL) {
    96 	    goto loser;
    97         }
    98         strcpy(rwlock->rw_name, lock_name);
    99     } else {
   100         rwlock->rw_name = NULL;
   101     }
   102     rwlock->rw_rank            = lock_rank;
   103     rwlock->rw_waiting_readers = 0;
   104     rwlock->rw_waiting_writers = 0;
   105     rwlock->rw_reader_locks    = 0;
   106     rwlock->rw_writer_locks    = 0;
   108     return rwlock;
   110 loser:
   111     NSSRWLock_Destroy(rwlock);
   112     return(NULL);
   113 }
   115 /*
   116 ** Destroy the given RWLock "lock".
   117 */
   118 void
   119 NSSRWLock_Destroy(NSSRWLock *rwlock)
   120 {
   121     PR_ASSERT(rwlock != NULL);
   122     PR_ASSERT(rwlock->rw_waiting_readers == 0);
   124     /* XXX Shouldn't we lock the PZLock before destroying this?? */
   126     if (rwlock->rw_name)
   127     	PR_Free(rwlock->rw_name);
   128     if (rwlock->rw_reader_waitq)
   129     	PZ_DestroyCondVar(rwlock->rw_reader_waitq);
   130     if (rwlock->rw_writer_waitq)
   131 	PZ_DestroyCondVar(rwlock->rw_writer_waitq);
   132     if (rwlock->rw_lock)
   133 	PZ_DestroyLock(rwlock->rw_lock);
   134     PR_DELETE(rwlock);
   135 }
   137 /*
   138 ** Read-lock the RWLock.
   139 */
   140 void
   141 NSSRWLock_LockRead(NSSRWLock *rwlock)
   142 {
   143     PRThread *me = PR_GetCurrentThread();
   145     PZ_Lock(rwlock->rw_lock);
   146 #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
   148     /*
   149      * assert that rank ordering is not violated; the rank of 'rwlock' should
   150      * be equal to or greater than the highest rank of all the locks held by
   151      * the thread.
   152      */
   153     PR_ASSERT((rwlock->rw_rank == NSS_RWLOCK_RANK_NONE) ||
   154               (rwlock->rw_rank >= nssRWLock_GetThreadRank(me)));
   155 #endif
   156     /*
   157      * wait if write-locked or if a writer is waiting; preference for writers
   158      */
   159     UNTIL ( (rwlock->rw_owner == me) ||		  /* I own it, or        */
   160 	   ((rwlock->rw_owner == NULL) &&	  /* no-one owns it, and */
   161 	    (rwlock->rw_waiting_writers == 0))) { /* no-one is waiting to own */
   163 	rwlock->rw_waiting_readers++;
   164 	PZ_WaitCondVar(rwlock->rw_reader_waitq, PR_INTERVAL_NO_TIMEOUT);
   165 	rwlock->rw_waiting_readers--;
   166     }
   167     rwlock->rw_reader_locks++; 		/* Increment read-lock count */
   169     PZ_Unlock(rwlock->rw_lock);
   171 #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
   172     nssRWLock_SetThreadRank(me, rwlock);/* update thread's lock rank */
   173 #endif
   174 }
   176 /* Unlock a Read lock held on this RW lock.
   177 */
   178 void
   179 NSSRWLock_UnlockRead(NSSRWLock *rwlock)
   180 {
   181     PZ_Lock(rwlock->rw_lock);
   183     PR_ASSERT(rwlock->rw_reader_locks > 0); /* lock must be read locked */
   185     if ((  rwlock->rw_reader_locks  > 0)  &&	/* caller isn't screwey */
   186         (--rwlock->rw_reader_locks == 0)  &&	/* not read locked any more */
   187 	(  rwlock->rw_owner        == NULL) &&	/* not write locked */
   188 	(  rwlock->rw_waiting_writers > 0)) {	/* someone's waiting. */
   190 	PZ_NotifyCondVar(rwlock->rw_writer_waitq); /* wake him up. */
   191     }
   193     PZ_Unlock(rwlock->rw_lock);
   195 #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
   196     /*
   197      * update thread's lock rank
   198      */
   199     nssRWLock_UnsetThreadRank(me, rwlock);
   200 #endif
   201     return;
   202 }
   204 /*
   205 ** Write-lock the RWLock.
   206 */
   207 void
   208 NSSRWLock_LockWrite(NSSRWLock *rwlock)
   209 {
   210     PRThread *me = PR_GetCurrentThread();
   212     PZ_Lock(rwlock->rw_lock);
   213 #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
   214     /*
   215      * assert that rank ordering is not violated; the rank of 'rwlock' should
   216      * be equal to or greater than the highest rank of all the locks held by
   217      * the thread.
   218      */
   219     PR_ASSERT((rwlock->rw_rank == NSS_RWLOCK_RANK_NONE) ||
   220                     (rwlock->rw_rank >= nssRWLock_GetThreadRank(me)));
   221 #endif
   222     /*
   223      * wait if read locked or write locked.
   224      */
   225     PR_ASSERT(rwlock->rw_reader_locks >= 0);
   226     PR_ASSERT(me != NULL);
   228     UNTIL ( (rwlock->rw_owner == me) ||           /* I own write lock, or */
   229 	   ((rwlock->rw_owner == NULL) &&	  /* no writer   and */
   230 	    (rwlock->rw_reader_locks == 0))) {    /* no readers, either. */
   232         rwlock->rw_waiting_writers++;
   233         PZ_WaitCondVar(rwlock->rw_writer_waitq, PR_INTERVAL_NO_TIMEOUT);
   234         rwlock->rw_waiting_writers--;
   235 	PR_ASSERT(rwlock->rw_reader_locks >= 0);
   236     }
   238     PR_ASSERT(rwlock->rw_reader_locks == 0);
   239     /*
   240      * apply write lock
   241      */
   242     rwlock->rw_owner = me;
   243     rwlock->rw_writer_locks++; 		/* Increment write-lock count */
   245     PZ_Unlock(rwlock->rw_lock);
   247 #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
   248     /*
   249      * update thread's lock rank
   250      */
   251     nssRWLock_SetThreadRank(me,rwlock);
   252 #endif
   253 }
   255 /* Unlock a Read lock held on this RW lock.
   256 */
   257 void
   258 NSSRWLock_UnlockWrite(NSSRWLock *rwlock)
   259 {
   260     PRThread *me = PR_GetCurrentThread();
   262     PZ_Lock(rwlock->rw_lock);
   263     PR_ASSERT(rwlock->rw_owner == me); /* lock must be write-locked by me.  */
   264     PR_ASSERT(rwlock->rw_writer_locks > 0); /* lock must be write locked */
   266     if (  rwlock->rw_owner        == me  &&	/* I own it, and            */
   267           rwlock->rw_writer_locks  > 0   &&	/* I own it, and            */
   268         --rwlock->rw_writer_locks == 0) {	/* I'm all done with it     */
   270 	rwlock->rw_owner = NULL;		/* I don't own it any more. */
   272 	/* Give preference to waiting writers. */
   273 	if (rwlock->rw_waiting_writers > 0) {
   274 	    if (rwlock->rw_reader_locks == 0)
   275 		PZ_NotifyCondVar(rwlock->rw_writer_waitq);
   276 	} else if (rwlock->rw_waiting_readers > 0) {
   277 	    PZ_NotifyAllCondVar(rwlock->rw_reader_waitq);
   278 	}
   279     }
   280     PZ_Unlock(rwlock->rw_lock);
   282 #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
   283     /*
   284      * update thread's lock rank
   285      */
   286     nssRWLock_UnsetThreadRank(me, rwlock);
   287 #endif
   288     return;
   289 }
   291 /* This is primarily for debugging, i.e. for inclusion in ASSERT calls. */
   292 PRBool
   293 NSSRWLock_HaveWriteLock(NSSRWLock *rwlock) {
   294     PRBool ownWriteLock;
   295     PRThread *me = PR_GetCurrentThread();
   297     /* This lock call isn't really necessary.
   298     ** If this thread is the owner, that fact cannot change during this call,
   299     ** because this thread is in this call.
   300     ** If this thread is NOT the owner, the owner could change, but it 
   301     ** could not become this thread.  
   302     */
   303 #if UNNECESSARY
   304     PZ_Lock(rwlock->rw_lock);	
   305 #endif
   306     ownWriteLock = (PRBool)(me == rwlock->rw_owner);
   307 #if UNNECESSARY
   308     PZ_Unlock(rwlock->rw_lock);
   309 #endif
   310     return ownWriteLock;
   311 }
   313 #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
   315 /*
   316  * nssRWLock_SetThreadRank
   317  *      Set a thread's lock rank, which is the highest of the ranks of all
   318  *      the locks held by the thread. Pointers to the locks are added to a
   319  *      per-thread list, which is anchored off a thread-private data key.
   320  */
   322 static void
   323 nssRWLock_SetThreadRank(PRThread *me, NSSRWLock *rwlock)
   324 {
   325     thread_rwlock_stack *lock_stack;
   326     PRStatus rv;
   328     /*
   329      * allocated thread-private-data for rwlock list, if not already allocated
   330      */
   331     if (!nss_thread_rwlock_initialized) {
   332         /*
   333          * allocate tpd, only if not failed already
   334          */
   335         if (!nss_thread_rwlock_alloc_failed) {
   336             if (PR_NewThreadPrivateIndex(&nss_thread_rwlock,
   337                                         nssRWLock_ReleaseLockStack)
   338                                                 == PR_FAILURE) {
   339                 nss_thread_rwlock_alloc_failed = 1;
   340                 return;
   341             }
   342         } else
   343             return;
   344     }
   345     /*
   346      * allocate a lock stack
   347      */
   348     if ((lock_stack = PR_GetThreadPrivate(nss_thread_rwlock)) == NULL) {
   349         lock_stack = (thread_rwlock_stack *)
   350                         PR_CALLOC(1 * sizeof(thread_rwlock_stack));
   351         if (lock_stack) {
   352             rv = PR_SetThreadPrivate(nss_thread_rwlock, lock_stack);
   353             if (rv == PR_FAILURE) {
   354                 PR_DELETE(lock_stack);
   355                 nss_thread_rwlock_alloc_failed = 1;
   356                 return;
   357             }
   358         } else {
   359             nss_thread_rwlock_alloc_failed = 1;
   360             return;
   361         }
   362     }
   363     /*
   364      * add rwlock to lock stack, if limit is not exceeded
   365      */
   366     if (lock_stack) {
   367         if (lock_stack->trs_index < _NSS_RWLOCK_RANK_ORDER_LIMIT)
   368             lock_stack->trs_stack[lock_stack->trs_index++] = rwlock;
   369     }
   370     nss_thread_rwlock_initialized = 1;
   371 }
   373 static void
   374 nssRWLock_ReleaseLockStack(void *lock_stack)
   375 {
   376     PR_ASSERT(lock_stack);
   377     PR_DELETE(lock_stack);
   378 }
   380 /*
   381  * nssRWLock_GetThreadRank
   382  *
   383  *      return thread's lock rank. If thread-private-data for the lock
   384  *      stack is not allocated, return NSS_RWLOCK_RANK_NONE.
   385  */
   387 static PRUint32
   388 nssRWLock_GetThreadRank(PRThread *me)
   389 {
   390     thread_rwlock_stack *lock_stack;
   392     if (nss_thread_rwlock_initialized) {
   393         if ((lock_stack = PR_GetThreadPrivate(nss_thread_rwlock)) == NULL)
   394             return (NSS_RWLOCK_RANK_NONE);
   395         else
   396             return(lock_stack->trs_stack[lock_stack->trs_index - 1]->rw_rank);
   398     } else
   399             return (NSS_RWLOCK_RANK_NONE);
   400 }
   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  */
   409 static void
   410 nssRWLock_UnsetThreadRank(PRThread *me, NSSRWLock *rwlock)
   411 {
   412     thread_rwlock_stack *lock_stack;
   413     int new_index = 0, index, done = 0;
   415     if (!nss_thread_rwlock_initialized)
   416         return;
   418     lock_stack = PR_GetThreadPrivate(nss_thread_rwlock);
   420     PR_ASSERT(lock_stack != NULL);
   422     index = lock_stack->trs_index - 1;
   423     while (index-- >= 0) {
   424         if ((lock_stack->trs_stack[index] == rwlock) && !done)  {
   425             /*
   426              * reset the slot for rwlock
   427              */
   428             lock_stack->trs_stack[index] = NULL;
   429             done = 1;
   430         }
   431         /*
   432          * search for the lowest-numbered empty slot, above which there are
   433          * no non-empty slots
   434          */
   435         if ((lock_stack->trs_stack[index] != NULL) && !new_index)
   436             new_index = index + 1;
   437         if (done && new_index)
   438             break;
   439     }
   440     /*
   441      * set top of stack to highest numbered empty slot
   442      */
   443     lock_stack->trs_index = new_index;
   445 }
   447 #endif  /* NSS_RWLOCK_RANK_ORDER_DEBUG */

mercurial