1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is the Netscape Portable Runtime (NSPR).
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
42 #if defined(HPUX) && defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
45 #define HAVE_UNIX98_RWLOCK
46 #define RWLOCK_T pthread_rwlock_t
47 #define RWLOCK_INIT(lock) pthread_rwlock_init(lock, NULL)
48 #define RWLOCK_DESTROY(lock) pthread_rwlock_destroy(lock)
49 #define RWLOCK_RDLOCK(lock) pthread_rwlock_rdlock(lock)
50 #define RWLOCK_WRLOCK(lock) pthread_rwlock_wrlock(lock)
51 #define RWLOCK_UNLOCK(lock) pthread_rwlock_unlock(lock)
53 #elif defined(SOLARIS) && (defined(_PR_PTHREADS) \
54 || defined(_PR_GLOBAL_THREADS_ONLY))
57 #define HAVE_UI_RWLOCK
58 #define RWLOCK_T rwlock_t
59 #define RWLOCK_INIT(lock) rwlock_init(lock, USYNC_THREAD, NULL)
60 #define RWLOCK_DESTROY(lock) rwlock_destroy(lock)
61 #define RWLOCK_RDLOCK(lock) rw_rdlock(lock)
62 #define RWLOCK_WRLOCK(lock) rw_wrlock(lock)
63 #define RWLOCK_UNLOCK(lock) rw_unlock(lock)
71 char *rw_name
; /* lock name */
72 PRUint32 rw_rank
; /* rank of the lock */
74 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
78 PRInt32 rw_lock_cnt
; /* == 0, if unlocked */
79 /* == -1, if write-locked */
80 /* > 0 , # of read locks */
81 PRUint32 rw_reader_cnt
; /* number of waiting readers */
82 PRUint32 rw_writer_cnt
; /* number of waiting writers */
83 PRCondVar
*rw_reader_waitq
; /* cvar for readers */
84 PRCondVar
*rw_writer_waitq
; /* cvar for writers */
86 PRThread
*rw_owner
; /* lock owner for write-lock */
92 #define _PR_RWLOCK_RANK_ORDER_DEBUG /* enable deadlock detection using
97 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
99 static PRUintn pr_thread_rwlock_key
; /* TPD key for lock stack */
100 static PRUintn pr_thread_rwlock_alloc_failed
;
102 #define _PR_RWLOCK_RANK_ORDER_LIMIT 10
104 typedef struct thread_rwlock_stack
{
105 PRInt32 trs_index
; /* top of stack */
106 PRRWLock
*trs_stack
[_PR_RWLOCK_RANK_ORDER_LIMIT
]; /* stack of lock
109 } thread_rwlock_stack
;
111 static void _PR_SET_THREAD_RWLOCK_RANK(PRRWLock
*rwlock
);
112 static PRUint32
_PR_GET_THREAD_RWLOCK_RANK(void);
113 static void _PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock
*rwlock
);
114 static void _PR_RELEASE_LOCK_STACK(void *lock_stack
);
119 * Reader/Writer Locks
124 * Create a reader-writer lock, with the given lock rank and lock name
128 PR_IMPLEMENT(PRRWLock
*)
129 PR_NewRWLock(PRUint32 lock_rank
, const char *lock_name
)
132 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
136 if (!_pr_initialized
) _PR_ImplicitInitialization();
138 rwlock
= PR_NEWZAP(PRRWLock
);
142 rwlock
->rw_rank
= lock_rank
;
143 if (lock_name
!= NULL
) {
144 rwlock
->rw_name
= (char*) PR_Malloc(strlen(lock_name
) + 1);
145 if (rwlock
->rw_name
== NULL
) {
149 strcpy(rwlock
->rw_name
, lock_name
);
151 rwlock
->rw_name
= NULL
;
154 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
155 err
= RWLOCK_INIT(&rwlock
->rw_lock
);
157 PR_SetError(PR_UNKNOWN_ERROR
, err
);
158 PR_Free(rwlock
->rw_name
);
164 rwlock
->rw_lock
= PR_NewLock();
165 if (rwlock
->rw_lock
== NULL
) {
168 rwlock
->rw_reader_waitq
= PR_NewCondVar(rwlock
->rw_lock
);
169 if (rwlock
->rw_reader_waitq
== NULL
) {
172 rwlock
->rw_writer_waitq
= PR_NewCondVar(rwlock
->rw_lock
);
173 if (rwlock
->rw_writer_waitq
== NULL
) {
176 rwlock
->rw_reader_cnt
= 0;
177 rwlock
->rw_writer_cnt
= 0;
178 rwlock
->rw_lock_cnt
= 0;
182 if (rwlock
->rw_reader_waitq
!= NULL
) {
183 PR_DestroyCondVar(rwlock
->rw_reader_waitq
);
185 if (rwlock
->rw_lock
!= NULL
) {
186 PR_DestroyLock(rwlock
->rw_lock
);
188 PR_Free(rwlock
->rw_name
);
195 ** Destroy the given RWLock "lock".
198 PR_DestroyRWLock(PRRWLock
*rwlock
)
200 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
202 err
= RWLOCK_DESTROY(&rwlock
->rw_lock
);
205 PR_ASSERT(rwlock
->rw_reader_cnt
== 0);
206 PR_DestroyCondVar(rwlock
->rw_reader_waitq
);
207 PR_DestroyCondVar(rwlock
->rw_writer_waitq
);
208 PR_DestroyLock(rwlock
->rw_lock
);
210 if (rwlock
->rw_name
!= NULL
)
211 PR_Free(rwlock
->rw_name
);
216 ** Read-lock the RWLock.
219 PR_RWLock_Rlock(PRRWLock
*rwlock
)
221 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
225 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
227 * assert that rank ordering is not violated; the rank of 'rwlock' should
228 * be equal to or greater than the highest rank of all the locks held by
231 PR_ASSERT((rwlock
->rw_rank
== PR_RWLOCK_RANK_NONE
) ||
232 (rwlock
->rw_rank
>= _PR_GET_THREAD_RWLOCK_RANK()));
235 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
236 err
= RWLOCK_RDLOCK(&rwlock
->rw_lock
);
239 PR_Lock(rwlock
->rw_lock
);
241 * wait if write-locked or if a writer is waiting; preference for writers
243 while ((rwlock
->rw_lock_cnt
< 0) ||
244 (rwlock
->rw_writer_cnt
> 0)) {
245 rwlock
->rw_reader_cnt
++;
246 PR_WaitCondVar(rwlock
->rw_reader_waitq
, PR_INTERVAL_NO_TIMEOUT
);
247 rwlock
->rw_reader_cnt
--;
250 * Increment read-lock count
252 rwlock
->rw_lock_cnt
++;
254 PR_Unlock(rwlock
->rw_lock
);
257 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
259 * update thread's lock rank
261 _PR_SET_THREAD_RWLOCK_RANK(rwlock
);
266 ** Write-lock the RWLock.
269 PR_RWLock_Wlock(PRRWLock
*rwlock
)
272 PRThread
*me
= PR_GetCurrentThread();
274 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
278 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
280 * assert that rank ordering is not violated; the rank of 'rwlock' should
281 * be equal to or greater than the highest rank of all the locks held by
284 PR_ASSERT((rwlock
->rw_rank
== PR_RWLOCK_RANK_NONE
) ||
285 (rwlock
->rw_rank
>= _PR_GET_THREAD_RWLOCK_RANK()));
288 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
289 err
= RWLOCK_WRLOCK(&rwlock
->rw_lock
);
292 PR_Lock(rwlock
->rw_lock
);
294 * wait if read locked
296 while (rwlock
->rw_lock_cnt
!= 0) {
297 rwlock
->rw_writer_cnt
++;
298 PR_WaitCondVar(rwlock
->rw_writer_waitq
, PR_INTERVAL_NO_TIMEOUT
);
299 rwlock
->rw_writer_cnt
--;
304 rwlock
->rw_lock_cnt
--;
305 PR_ASSERT(rwlock
->rw_lock_cnt
== -1);
307 PR_ASSERT(me
!= NULL
);
308 rwlock
->rw_owner
= me
;
310 PR_Unlock(rwlock
->rw_lock
);
313 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
315 * update thread's lock rank
317 _PR_SET_THREAD_RWLOCK_RANK(rwlock
);
322 ** Unlock the RW lock.
325 PR_RWLock_Unlock(PRRWLock
*rwlock
)
328 PRThread
*me
= PR_GetCurrentThread();
330 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
334 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
335 err
= RWLOCK_UNLOCK(&rwlock
->rw_lock
);
338 PR_Lock(rwlock
->rw_lock
);
340 * lock must be read or write-locked
342 PR_ASSERT(rwlock
->rw_lock_cnt
!= 0);
343 if (rwlock
->rw_lock_cnt
> 0) {
346 * decrement read-lock count
348 rwlock
->rw_lock_cnt
--;
349 if (rwlock
->rw_lock_cnt
== 0) {
351 * lock is not read-locked anymore; wakeup a waiting writer
353 if (rwlock
->rw_writer_cnt
> 0)
354 PR_NotifyCondVar(rwlock
->rw_writer_waitq
);
357 PR_ASSERT(rwlock
->rw_lock_cnt
== -1);
359 rwlock
->rw_lock_cnt
= 0;
361 PR_ASSERT(rwlock
->rw_owner
== me
);
362 rwlock
->rw_owner
= NULL
;
365 * wakeup a writer, if present; preference for writers
367 if (rwlock
->rw_writer_cnt
> 0)
368 PR_NotifyCondVar(rwlock
->rw_writer_waitq
);
370 * else, wakeup all readers, if any
372 else if (rwlock
->rw_reader_cnt
> 0)
373 PR_NotifyAllCondVar(rwlock
->rw_reader_waitq
);
375 PR_Unlock(rwlock
->rw_lock
);
378 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
380 * update thread's lock rank
382 _PR_UNSET_THREAD_RWLOCK_RANK(rwlock
);
387 #ifndef _PR_RWLOCK_RANK_ORDER_DEBUG
389 void _PR_InitRWLocks(void) { }
393 void _PR_InitRWLocks(void)
396 * allocated thread-private-data index for rwlock list
398 if (PR_NewThreadPrivateIndex(&pr_thread_rwlock_key
,
399 _PR_RELEASE_LOCK_STACK
) == PR_FAILURE
) {
400 pr_thread_rwlock_alloc_failed
= 1;
406 * _PR_SET_THREAD_RWLOCK_RANK
407 * Set a thread's lock rank, which is the highest of the ranks of all
408 * the locks held by the thread. Pointers to the locks are added to a
409 * per-thread list, which is anchored off a thread-private data key.
413 _PR_SET_THREAD_RWLOCK_RANK(PRRWLock
*rwlock
)
415 thread_rwlock_stack
*lock_stack
;
419 * allocate a lock stack
421 if ((lock_stack
= PR_GetThreadPrivate(pr_thread_rwlock_key
)) == NULL
) {
422 lock_stack
= (thread_rwlock_stack
*)
423 PR_CALLOC(1 * sizeof(thread_rwlock_stack
));
425 rv
= PR_SetThreadPrivate(pr_thread_rwlock_key
, lock_stack
);
426 if (rv
== PR_FAILURE
) {
427 PR_DELETE(lock_stack
);
428 pr_thread_rwlock_alloc_failed
= 1;
432 pr_thread_rwlock_alloc_failed
= 1;
437 * add rwlock to lock stack, if limit is not exceeded
440 if (lock_stack
->trs_index
< _PR_RWLOCK_RANK_ORDER_LIMIT
)
441 lock_stack
->trs_stack
[lock_stack
->trs_index
++] = rwlock
;
446 _PR_RELEASE_LOCK_STACK(void *lock_stack
)
448 PR_ASSERT(lock_stack
);
449 PR_DELETE(lock_stack
);
453 * _PR_GET_THREAD_RWLOCK_RANK
455 * return thread's lock rank. If thread-private-data for the lock
456 * stack is not allocated, return PR_RWLOCK_RANK_NONE.
460 _PR_GET_THREAD_RWLOCK_RANK(void)
462 thread_rwlock_stack
*lock_stack
;
464 if ((lock_stack
= PR_GetThreadPrivate(pr_thread_rwlock_key
)) == NULL
)
465 return (PR_RWLOCK_RANK_NONE
);
467 return(lock_stack
->trs_stack
[lock_stack
->trs_index
- 1]->rw_rank
);
471 * _PR_UNSET_THREAD_RWLOCK_RANK
473 * remove the rwlock from the lock stack. Since locks may not be
474 * unlocked in a FIFO order, the entire lock stack is searched.
478 _PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock
*rwlock
)
480 thread_rwlock_stack
*lock_stack
;
481 int new_index
= 0, index
, done
= 0;
483 lock_stack
= PR_GetThreadPrivate(pr_thread_rwlock_key
);
485 PR_ASSERT(lock_stack
!= NULL
);
487 index
= lock_stack
->trs_index
- 1;
488 while (index
-- >= 0) {
489 if ((lock_stack
->trs_stack
[index
] == rwlock
) && !done
) {
491 * reset the slot for rwlock
493 lock_stack
->trs_stack
[index
] = NULL
;
497 * search for the lowest-numbered empty slot, above which there are
500 if ((lock_stack
->trs_stack
[index
] != NULL
) && !new_index
)
501 new_index
= index
+ 1;
502 if (done
&& new_index
)
506 * set top of stack to highest numbered empty slot
508 lock_stack
->trs_index
= new_index
;
512 #endif /* _PR_RWLOCK_RANK_ORDER_DEBUG */