1
/* ***** BEGIN LICENSE BLOCK *****
2
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
4
* The contents of this file are subject to the Mozilla Public License Version
5
* 1.1 (the "License"); you may not use this file except in compliance with
6
* the License. You may obtain a copy of the License at
7
* http://www.mozilla.org/MPL/
9
* Software distributed under the License is distributed on an "AS IS" basis,
10
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11
* for the specific language governing rights and limitations under the
14
* The Original Code is the Netscape security libraries.
16
* The Initial Developer of the Original Code is
17
* Netscape Communications Corporation.
18
* Portions created by the Initial Developer are Copyright (C) 1994-2000
19
* the Initial Developer. All Rights Reserved.
23
* Alternatively, the contents of this file may be used under the terms of
24
* either the GNU General Public License Version 2 or later (the "GPL"), or
25
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26
* in which case the provisions of the GPL or the LGPL are applicable instead
27
* of those above. If you wish to allow use of your version of this file only
28
* under the terms of either the GPL or the LGPL, and not to allow others to
29
* use your version of this file under the terms of the MPL, indicate your
30
* decision by deleting the provisions above and replace them with the notice
31
* and other provisions required by the GPL or the LGPL. If you do not delete
32
* the provisions above, a recipient may use your version of this file under
33
* the terms of any one of the MPL, the GPL or the LGPL.
35
* ***** END LICENSE BLOCK ***** */
47
char * rw_name; /* lock name */
48
PRUint32 rw_rank; /* rank of the lock */
49
PRInt32 rw_writer_locks; /* == 0, if unlocked */
50
PRInt32 rw_reader_locks; /* == 0, if unlocked */
51
/* > 0 , # of read locks */
52
PRUint32 rw_waiting_readers; /* number of waiting readers */
53
PRUint32 rw_waiting_writers; /* number of waiting writers */
54
PZCondVar * rw_reader_waitq; /* cvar for readers */
55
PZCondVar * rw_writer_waitq; /* cvar for writers */
56
PRThread * rw_owner; /* lock owner for write-lock */
57
/* Non-null if write lock held. */
64
#ifdef DEBUG_RANK_ORDER
65
#define NSS_RWLOCK_RANK_ORDER_DEBUG /* enable deadlock detection using
70
#ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
72
static PRUintn nss_thread_rwlock_initialized;
73
static PRUintn nss_thread_rwlock; /* TPD key for lock stack */
74
static PRUintn nss_thread_rwlock_alloc_failed;
76
#define _NSS_RWLOCK_RANK_ORDER_LIMIT 10
78
typedef struct thread_rwlock_stack {
79
PRInt32 trs_index; /* top of stack */
80
NSSRWLock *trs_stack[_NSS_RWLOCK_RANK_ORDER_LIMIT]; /* stack of lock
82
} thread_rwlock_stack;
84
/* forward static declarations. */
85
static PRUint32 nssRWLock_GetThreadRank(PRThread *me);
86
static void nssRWLock_SetThreadRank(PRThread *me, NSSRWLock *rwlock);
87
static void nssRWLock_UnsetThreadRank(PRThread *me, NSSRWLock *rwlock);
88
static void nssRWLock_ReleaseLockStack(void *lock_stack);
92
#define UNTIL(x) while(!(x))
100
* Create a reader-writer lock, with the given lock rank and lock name
104
PR_IMPLEMENT(NSSRWLock *)
105
NSSRWLock_New(PRUint32 lock_rank, const char *lock_name)
109
rwlock = PR_NEWZAP(NSSRWLock);
113
rwlock->rw_lock = PZ_NewLock(nssILockRWLock);
114
if (rwlock->rw_lock == NULL) {
117
rwlock->rw_reader_waitq = PZ_NewCondVar(rwlock->rw_lock);
118
if (rwlock->rw_reader_waitq == NULL) {
121
rwlock->rw_writer_waitq = PZ_NewCondVar(rwlock->rw_lock);
122
if (rwlock->rw_writer_waitq == NULL) {
125
if (lock_name != NULL) {
126
rwlock->rw_name = (char*) PR_Malloc(strlen(lock_name) + 1);
127
if (rwlock->rw_name == NULL) {
130
strcpy(rwlock->rw_name, lock_name);
132
rwlock->rw_name = NULL;
134
rwlock->rw_rank = lock_rank;
135
rwlock->rw_waiting_readers = 0;
136
rwlock->rw_waiting_writers = 0;
137
rwlock->rw_reader_locks = 0;
138
rwlock->rw_writer_locks = 0;
143
NSSRWLock_Destroy(rwlock);
148
** Destroy the given RWLock "lock".
151
NSSRWLock_Destroy(NSSRWLock *rwlock)
153
PR_ASSERT(rwlock != NULL);
154
PR_ASSERT(rwlock->rw_waiting_readers == 0);
156
/* XXX Shouldn't we lock the PZLock before destroying this?? */
159
PR_Free(rwlock->rw_name);
160
if (rwlock->rw_reader_waitq)
161
PZ_DestroyCondVar(rwlock->rw_reader_waitq);
162
if (rwlock->rw_writer_waitq)
163
PZ_DestroyCondVar(rwlock->rw_writer_waitq);
165
PZ_DestroyLock(rwlock->rw_lock);
169
/***********************************************************************
170
** Given the address of a NULL pointer to a NSSRWLock,
171
** atomically initializes that pointer to a newly created NSSRWLock.
172
** Returns the value placed into that pointer, or NULL.
173
** If the lock cannot be created because of resource constraints,
174
** the pointer will be left NULL.
176
***********************************************************************/
177
PR_IMPLEMENT(NSSRWLock *)
178
nssRWLock_AtomicCreate( NSSRWLock ** prwlock,
180
const char * lock_name)
183
static PRInt32 initializers;
185
PR_ASSERT(prwlock != NULL);
187
/* atomically initialize the lock */
188
while (NULL == (rwlock = *prwlock)) {
189
PRInt32 myAttempt = PR_AtomicIncrement(&initializers);
190
if (myAttempt == 1) {
191
if (NULL == (rwlock = *prwlock)) {
192
*prwlock = rwlock = NSSRWLock_New(lock_rank, lock_name);
194
(void) PR_AtomicDecrement(&initializers);
197
PR_Sleep(PR_INTERVAL_NO_WAIT); /* PR_Yield() */
198
(void) PR_AtomicDecrement(&initializers);
205
** Read-lock the RWLock.
208
NSSRWLock_LockRead(NSSRWLock *rwlock)
210
PRThread *me = PR_GetCurrentThread();
212
PZ_Lock(rwlock->rw_lock);
213
#ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
216
* assert that rank ordering is not violated; the rank of 'rwlock' should
217
* be equal to or greater than the highest rank of all the locks held by
220
PR_ASSERT((rwlock->rw_rank == NSS_RWLOCK_RANK_NONE) ||
221
(rwlock->rw_rank >= nssRWLock_GetThreadRank(me)));
224
* wait if write-locked or if a writer is waiting; preference for writers
226
UNTIL ( (rwlock->rw_owner == me) || /* I own it, or */
227
((rwlock->rw_owner == NULL) && /* no-one owns it, and */
228
(rwlock->rw_waiting_writers == 0))) { /* no-one is waiting to own */
230
rwlock->rw_waiting_readers++;
231
PZ_WaitCondVar(rwlock->rw_reader_waitq, PR_INTERVAL_NO_TIMEOUT);
232
rwlock->rw_waiting_readers--;
234
rwlock->rw_reader_locks++; /* Increment read-lock count */
236
PZ_Unlock(rwlock->rw_lock);
238
#ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
239
nssRWLock_SetThreadRank(me, rwlock);/* update thread's lock rank */
243
/* Unlock a Read lock held on this RW lock.
246
NSSRWLock_UnlockRead(NSSRWLock *rwlock)
248
PZ_Lock(rwlock->rw_lock);
250
PR_ASSERT(rwlock->rw_reader_locks > 0); /* lock must be read locked */
252
if (( rwlock->rw_reader_locks > 0) && /* caller isn't screwey */
253
(--rwlock->rw_reader_locks == 0) && /* not read locked any more */
254
( rwlock->rw_owner == NULL) && /* not write locked */
255
( rwlock->rw_waiting_writers > 0)) { /* someone's waiting. */
257
PZ_NotifyCondVar(rwlock->rw_writer_waitq); /* wake him up. */
260
PZ_Unlock(rwlock->rw_lock);
262
#ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
264
* update thread's lock rank
266
nssRWLock_UnsetThreadRank(me, rwlock);
272
** Write-lock the RWLock.
275
NSSRWLock_LockWrite(NSSRWLock *rwlock)
277
PRThread *me = PR_GetCurrentThread();
279
PZ_Lock(rwlock->rw_lock);
280
#ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
282
* assert that rank ordering is not violated; the rank of 'rwlock' should
283
* be equal to or greater than the highest rank of all the locks held by
286
PR_ASSERT((rwlock->rw_rank == NSS_RWLOCK_RANK_NONE) ||
287
(rwlock->rw_rank >= nssRWLock_GetThreadRank(me)));
290
* wait if read locked or write locked.
292
PR_ASSERT(rwlock->rw_reader_locks >= 0);
293
PR_ASSERT(me != NULL);
295
UNTIL ( (rwlock->rw_owner == me) || /* I own write lock, or */
296
((rwlock->rw_owner == NULL) && /* no writer and */
297
(rwlock->rw_reader_locks == 0))) { /* no readers, either. */
299
rwlock->rw_waiting_writers++;
300
PZ_WaitCondVar(rwlock->rw_writer_waitq, PR_INTERVAL_NO_TIMEOUT);
301
rwlock->rw_waiting_writers--;
302
PR_ASSERT(rwlock->rw_reader_locks >= 0);
305
PR_ASSERT(rwlock->rw_reader_locks == 0);
309
rwlock->rw_owner = me;
310
rwlock->rw_writer_locks++; /* Increment write-lock count */
312
PZ_Unlock(rwlock->rw_lock);
314
#ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
316
* update thread's lock rank
318
nssRWLock_SetThreadRank(me,rwlock);
322
/* Unlock a Read lock held on this RW lock.
325
NSSRWLock_UnlockWrite(NSSRWLock *rwlock)
327
PRThread *me = PR_GetCurrentThread();
329
PZ_Lock(rwlock->rw_lock);
330
PR_ASSERT(rwlock->rw_owner == me); /* lock must be write-locked by me. */
331
PR_ASSERT(rwlock->rw_writer_locks > 0); /* lock must be write locked */
333
if ( rwlock->rw_owner == me && /* I own it, and */
334
rwlock->rw_writer_locks > 0 && /* I own it, and */
335
--rwlock->rw_writer_locks == 0) { /* I'm all done with it */
337
rwlock->rw_owner = NULL; /* I don't own it any more. */
339
/* Give preference to waiting writers. */
340
if (rwlock->rw_waiting_writers > 0) {
341
if (rwlock->rw_reader_locks == 0)
342
PZ_NotifyCondVar(rwlock->rw_writer_waitq);
343
} else if (rwlock->rw_waiting_readers > 0) {
344
PZ_NotifyAllCondVar(rwlock->rw_reader_waitq);
347
PZ_Unlock(rwlock->rw_lock);
349
#ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
351
* update thread's lock rank
353
nssRWLock_UnsetThreadRank(me, rwlock);
358
/* This is primarily for debugging, i.e. for inclusion in ASSERT calls. */
360
NSSRWLock_HaveWriteLock(NSSRWLock *rwlock) {
362
PRThread *me = PR_GetCurrentThread();
364
/* This lock call isn't really necessary.
365
** If this thread is the owner, that fact cannot change during this call,
366
** because this thread is in this call.
367
** If this thread is NOT the owner, the owner could change, but it
368
** could not become this thread.
371
PZ_Lock(rwlock->rw_lock);
373
ownWriteLock = (PRBool)(me == rwlock->rw_owner);
375
PZ_Unlock(rwlock->rw_lock);
380
#ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
383
* nssRWLock_SetThreadRank
384
* Set a thread's lock rank, which is the highest of the ranks of all
385
* the locks held by the thread. Pointers to the locks are added to a
386
* per-thread list, which is anchored off a thread-private data key.
390
nssRWLock_SetThreadRank(PRThread *me, NSSRWLock *rwlock)
392
thread_rwlock_stack *lock_stack;
396
* allocated thread-private-data for rwlock list, if not already allocated
398
if (!nss_thread_rwlock_initialized) {
400
* allocate tpd, only if not failed already
402
if (!nss_thread_rwlock_alloc_failed) {
403
if (PR_NewThreadPrivateIndex(&nss_thread_rwlock,
404
nssRWLock_ReleaseLockStack)
406
nss_thread_rwlock_alloc_failed = 1;
413
* allocate a lock stack
415
if ((lock_stack = PR_GetThreadPrivate(nss_thread_rwlock)) == NULL) {
416
lock_stack = (thread_rwlock_stack *)
417
PR_CALLOC(1 * sizeof(thread_rwlock_stack));
419
rv = PR_SetThreadPrivate(nss_thread_rwlock, lock_stack);
420
if (rv == PR_FAILURE) {
421
PR_DELETE(lock_stack);
422
nss_thread_rwlock_alloc_failed = 1;
426
nss_thread_rwlock_alloc_failed = 1;
431
* add rwlock to lock stack, if limit is not exceeded
434
if (lock_stack->trs_index < _NSS_RWLOCK_RANK_ORDER_LIMIT)
435
lock_stack->trs_stack[lock_stack->trs_index++] = rwlock;
437
nss_thread_rwlock_initialized = 1;
441
nssRWLock_ReleaseLockStack(void *lock_stack)
443
PR_ASSERT(lock_stack);
444
PR_DELETE(lock_stack);
448
* nssRWLock_GetThreadRank
450
* return thread's lock rank. If thread-private-data for the lock
451
* stack is not allocated, return NSS_RWLOCK_RANK_NONE.
455
nssRWLock_GetThreadRank(PRThread *me)
457
thread_rwlock_stack *lock_stack;
459
if (nss_thread_rwlock_initialized) {
460
if ((lock_stack = PR_GetThreadPrivate(nss_thread_rwlock)) == NULL)
461
return (NSS_RWLOCK_RANK_NONE);
463
return(lock_stack->trs_stack[lock_stack->trs_index - 1]->rw_rank);
466
return (NSS_RWLOCK_RANK_NONE);
470
* nssRWLock_UnsetThreadRank
472
* remove the rwlock from the lock stack. Since locks may not be
473
* unlocked in a FIFO order, the entire lock stack is searched.
477
nssRWLock_UnsetThreadRank(PRThread *me, NSSRWLock *rwlock)
479
thread_rwlock_stack *lock_stack;
480
int new_index = 0, index, done = 0;
482
if (!nss_thread_rwlock_initialized)
485
lock_stack = PR_GetThreadPrivate(nss_thread_rwlock);
487
PR_ASSERT(lock_stack != NULL);
489
index = lock_stack->trs_index - 1;
490
while (index-- >= 0) {
491
if ((lock_stack->trs_stack[index] == rwlock) && !done) {
493
* reset the slot for rwlock
495
lock_stack->trs_stack[index] = NULL;
499
* search for the lowest-numbered empty slot, above which there are
502
if ((lock_stack->trs_stack[index] != NULL) && !new_index)
503
new_index = index + 1;
504
if (done && new_index)
508
* set top of stack to highest numbered empty slot
510
lock_stack->trs_index = new_index;
514
#endif /* NSS_RWLOCK_RANK_ORDER_DEBUG */