Bug 464947 - Clear Recent History dialog should clear form data by timespan. r=mconnor
[wine-gecko.git] / nsprpub / pr / src / threads / prrwlock.c
blob35e0e3e7237b185d114602f654970e948fff85a4
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
13 * License.
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.
22 * Contributor(s):
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 ***** */
38 #include "primpl.h"
40 #include <string.h>
42 #if defined(HPUX) && defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
44 #include <pthread.h>
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))
56 #include <synch.h>
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)
65 #endif
68 * Reader-writer lock
70 struct PRRWLock {
71 char *rw_name; /* lock name */
72 PRUint32 rw_rank; /* rank of the lock */
74 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
75 RWLOCK_T rw_lock;
76 #else
77 PRLock *rw_lock;
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 */
85 #ifdef DEBUG
86 PRThread *rw_owner; /* lock owner for write-lock */
87 #endif
88 #endif
91 #ifdef DEBUG
92 #define _PR_RWLOCK_RANK_ORDER_DEBUG /* enable deadlock detection using
93 rank-order for locks
95 #endif
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
107 pointers */
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);
116 #endif
119 * Reader/Writer Locks
123 * PR_NewRWLock
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)
131 PRRWLock *rwlock;
132 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
133 int err;
134 #endif
136 if (!_pr_initialized) _PR_ImplicitInitialization();
138 rwlock = PR_NEWZAP(PRRWLock);
139 if (rwlock == NULL)
140 return NULL;
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) {
146 PR_DELETE(rwlock);
147 return(NULL);
149 strcpy(rwlock->rw_name, lock_name);
150 } else {
151 rwlock->rw_name = NULL;
154 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
155 err = RWLOCK_INIT(&rwlock->rw_lock);
156 if (err != 0) {
157 PR_SetError(PR_UNKNOWN_ERROR, err);
158 PR_Free(rwlock->rw_name);
159 PR_DELETE(rwlock);
160 return NULL;
162 return rwlock;
163 #else
164 rwlock->rw_lock = PR_NewLock();
165 if (rwlock->rw_lock == NULL) {
166 goto failed;
168 rwlock->rw_reader_waitq = PR_NewCondVar(rwlock->rw_lock);
169 if (rwlock->rw_reader_waitq == NULL) {
170 goto failed;
172 rwlock->rw_writer_waitq = PR_NewCondVar(rwlock->rw_lock);
173 if (rwlock->rw_writer_waitq == NULL) {
174 goto failed;
176 rwlock->rw_reader_cnt = 0;
177 rwlock->rw_writer_cnt = 0;
178 rwlock->rw_lock_cnt = 0;
179 return rwlock;
181 failed:
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);
189 PR_DELETE(rwlock);
190 return NULL;
191 #endif
195 ** Destroy the given RWLock "lock".
197 PR_IMPLEMENT(void)
198 PR_DestroyRWLock(PRRWLock *rwlock)
200 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
201 int err;
202 err = RWLOCK_DESTROY(&rwlock->rw_lock);
203 PR_ASSERT(err == 0);
204 #else
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);
209 #endif
210 if (rwlock->rw_name != NULL)
211 PR_Free(rwlock->rw_name);
212 PR_DELETE(rwlock);
216 ** Read-lock the RWLock.
218 PR_IMPLEMENT(void)
219 PR_RWLock_Rlock(PRRWLock *rwlock)
221 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
222 int err;
223 #endif
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
229 * the thread.
231 PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) ||
232 (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK()));
233 #endif
235 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
236 err = RWLOCK_RDLOCK(&rwlock->rw_lock);
237 PR_ASSERT(err == 0);
238 #else
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);
255 #endif
257 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
259 * update thread's lock rank
261 _PR_SET_THREAD_RWLOCK_RANK(rwlock);
262 #endif
266 ** Write-lock the RWLock.
268 PR_IMPLEMENT(void)
269 PR_RWLock_Wlock(PRRWLock *rwlock)
271 #if defined(DEBUG)
272 PRThread *me = PR_GetCurrentThread();
273 #endif
274 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
275 int err;
276 #endif
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
282 * the thread.
284 PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) ||
285 (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK()));
286 #endif
288 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
289 err = RWLOCK_WRLOCK(&rwlock->rw_lock);
290 PR_ASSERT(err == 0);
291 #else
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--;
302 * apply write lock
304 rwlock->rw_lock_cnt--;
305 PR_ASSERT(rwlock->rw_lock_cnt == -1);
306 #ifdef DEBUG
307 PR_ASSERT(me != NULL);
308 rwlock->rw_owner = me;
309 #endif
310 PR_Unlock(rwlock->rw_lock);
311 #endif
313 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
315 * update thread's lock rank
317 _PR_SET_THREAD_RWLOCK_RANK(rwlock);
318 #endif
322 ** Unlock the RW lock.
324 PR_IMPLEMENT(void)
325 PR_RWLock_Unlock(PRRWLock *rwlock)
327 #if defined(DEBUG)
328 PRThread *me = PR_GetCurrentThread();
329 #endif
330 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
331 int err;
332 #endif
334 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
335 err = RWLOCK_UNLOCK(&rwlock->rw_lock);
336 PR_ASSERT(err == 0);
337 #else
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);
356 } else {
357 PR_ASSERT(rwlock->rw_lock_cnt == -1);
359 rwlock->rw_lock_cnt = 0;
360 #ifdef DEBUG
361 PR_ASSERT(rwlock->rw_owner == me);
362 rwlock->rw_owner = NULL;
363 #endif
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);
376 #endif
378 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
380 * update thread's lock rank
382 _PR_UNSET_THREAD_RWLOCK_RANK(rwlock);
383 #endif
384 return;
387 #ifndef _PR_RWLOCK_RANK_ORDER_DEBUG
389 void _PR_InitRWLocks(void) { }
391 #else
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;
401 return;
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.
412 static void
413 _PR_SET_THREAD_RWLOCK_RANK(PRRWLock *rwlock)
415 thread_rwlock_stack *lock_stack;
416 PRStatus rv;
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));
424 if (lock_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;
429 return;
431 } else {
432 pr_thread_rwlock_alloc_failed = 1;
433 return;
437 * add rwlock to lock stack, if limit is not exceeded
439 if (lock_stack) {
440 if (lock_stack->trs_index < _PR_RWLOCK_RANK_ORDER_LIMIT)
441 lock_stack->trs_stack[lock_stack->trs_index++] = rwlock;
445 static void
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.
459 static PRUint32
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);
466 else
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.
477 static void
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;
494 done = 1;
497 * search for the lowest-numbered empty slot, above which there are
498 * no non-empty slots
500 if ((lock_stack->trs_stack[index] != NULL) && !new_index)
501 new_index = index + 1;
502 if (done && new_index)
503 break;
506 * set top of stack to highest numbered empty slot
508 lock_stack->trs_index = new_index;
512 #endif /* _PR_RWLOCK_RANK_ORDER_DEBUG */