Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "primpl.h"
7 :
8 : #include <string.h>
9 :
10 : #if defined(HPUX) && defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
11 :
12 : #include <pthread.h>
13 : #define HAVE_UNIX98_RWLOCK
14 : #define RWLOCK_T pthread_rwlock_t
15 : #define RWLOCK_INIT(lock) pthread_rwlock_init(lock, NULL)
16 : #define RWLOCK_DESTROY(lock) pthread_rwlock_destroy(lock)
17 : #define RWLOCK_RDLOCK(lock) pthread_rwlock_rdlock(lock)
18 : #define RWLOCK_WRLOCK(lock) pthread_rwlock_wrlock(lock)
19 : #define RWLOCK_UNLOCK(lock) pthread_rwlock_unlock(lock)
20 :
21 : #elif defined(SOLARIS) && (defined(_PR_PTHREADS) \
22 : || defined(_PR_GLOBAL_THREADS_ONLY))
23 :
24 : #include <synch.h>
25 : #define HAVE_UI_RWLOCK
26 : #define RWLOCK_T rwlock_t
27 : #define RWLOCK_INIT(lock) rwlock_init(lock, USYNC_THREAD, NULL)
28 : #define RWLOCK_DESTROY(lock) rwlock_destroy(lock)
29 : #define RWLOCK_RDLOCK(lock) rw_rdlock(lock)
30 : #define RWLOCK_WRLOCK(lock) rw_wrlock(lock)
31 : #define RWLOCK_UNLOCK(lock) rw_unlock(lock)
32 :
33 : #endif
34 :
35 : /*
36 : * Reader-writer lock
37 : */
38 : struct PRRWLock {
39 : char *rw_name; /* lock name */
40 : PRUint32 rw_rank; /* rank of the lock */
41 :
42 : #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
43 : RWLOCK_T rw_lock;
44 : #else
45 : PRLock *rw_lock;
46 : PRInt32 rw_lock_cnt; /* == 0, if unlocked */
47 : /* == -1, if write-locked */
48 : /* > 0 , # of read locks */
49 : PRUint32 rw_reader_cnt; /* number of waiting readers */
50 : PRUint32 rw_writer_cnt; /* number of waiting writers */
51 : PRCondVar *rw_reader_waitq; /* cvar for readers */
52 : PRCondVar *rw_writer_waitq; /* cvar for writers */
53 : #ifdef DEBUG
54 : PRThread *rw_owner; /* lock owner for write-lock */
55 : #endif
56 : #endif
57 : };
58 :
59 : #ifdef DEBUG
60 : #define _PR_RWLOCK_RANK_ORDER_DEBUG /* enable deadlock detection using
61 : rank-order for locks
62 : */
63 : #endif
64 :
65 : #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
66 :
67 : static PRUintn pr_thread_rwlock_key; /* TPD key for lock stack */
68 : static PRUintn pr_thread_rwlock_alloc_failed;
69 :
70 : #define _PR_RWLOCK_RANK_ORDER_LIMIT 10
71 :
72 : typedef struct thread_rwlock_stack {
73 : PRInt32 trs_index; /* top of stack */
74 : PRRWLock *trs_stack[_PR_RWLOCK_RANK_ORDER_LIMIT]; /* stack of lock
75 : pointers */
76 :
77 : } thread_rwlock_stack;
78 :
79 : static void _PR_SET_THREAD_RWLOCK_RANK(PRRWLock *rwlock);
80 : static PRUint32 _PR_GET_THREAD_RWLOCK_RANK(void);
81 : static void _PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock *rwlock);
82 : static void _PR_RELEASE_LOCK_STACK(void *lock_stack);
83 :
84 : #endif
85 :
86 : /*
87 : * Reader/Writer Locks
88 : */
89 :
90 : /*
91 : * PR_NewRWLock
92 : * Create a reader-writer lock, with the given lock rank and lock name
93 : *
94 : */
95 :
96 : PR_IMPLEMENT(PRRWLock *)
97 : PR_NewRWLock(PRUint32 lock_rank, const char *lock_name)
98 : {
99 : PRRWLock *rwlock;
100 : #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
101 : int err;
102 : #endif
103 :
104 0 : if (!_pr_initialized) _PR_ImplicitInitialization();
105 :
106 0 : rwlock = PR_NEWZAP(PRRWLock);
107 0 : if (rwlock == NULL)
108 0 : return NULL;
109 :
110 0 : rwlock->rw_rank = lock_rank;
111 0 : if (lock_name != NULL) {
112 0 : rwlock->rw_name = (char*) PR_Malloc(strlen(lock_name) + 1);
113 0 : if (rwlock->rw_name == NULL) {
114 0 : PR_DELETE(rwlock);
115 0 : return(NULL);
116 : }
117 0 : strcpy(rwlock->rw_name, lock_name);
118 : } else {
119 0 : rwlock->rw_name = NULL;
120 : }
121 :
122 : #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
123 : err = RWLOCK_INIT(&rwlock->rw_lock);
124 : if (err != 0) {
125 : PR_SetError(PR_UNKNOWN_ERROR, err);
126 : PR_Free(rwlock->rw_name);
127 : PR_DELETE(rwlock);
128 : return NULL;
129 : }
130 : return rwlock;
131 : #else
132 0 : rwlock->rw_lock = PR_NewLock();
133 0 : if (rwlock->rw_lock == NULL) {
134 0 : goto failed;
135 : }
136 0 : rwlock->rw_reader_waitq = PR_NewCondVar(rwlock->rw_lock);
137 0 : if (rwlock->rw_reader_waitq == NULL) {
138 0 : goto failed;
139 : }
140 0 : rwlock->rw_writer_waitq = PR_NewCondVar(rwlock->rw_lock);
141 0 : if (rwlock->rw_writer_waitq == NULL) {
142 0 : goto failed;
143 : }
144 0 : rwlock->rw_reader_cnt = 0;
145 0 : rwlock->rw_writer_cnt = 0;
146 0 : rwlock->rw_lock_cnt = 0;
147 0 : return rwlock;
148 :
149 : failed:
150 0 : if (rwlock->rw_reader_waitq != NULL) {
151 0 : PR_DestroyCondVar(rwlock->rw_reader_waitq);
152 : }
153 0 : if (rwlock->rw_lock != NULL) {
154 0 : PR_DestroyLock(rwlock->rw_lock);
155 : }
156 0 : PR_Free(rwlock->rw_name);
157 0 : PR_DELETE(rwlock);
158 0 : return NULL;
159 : #endif
160 : }
161 :
162 : /*
163 : ** Destroy the given RWLock "lock".
164 : */
165 : PR_IMPLEMENT(void)
166 : PR_DestroyRWLock(PRRWLock *rwlock)
167 : {
168 : #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
169 : int err;
170 : err = RWLOCK_DESTROY(&rwlock->rw_lock);
171 : PR_ASSERT(err == 0);
172 : #else
173 0 : PR_ASSERT(rwlock->rw_reader_cnt == 0);
174 0 : PR_DestroyCondVar(rwlock->rw_reader_waitq);
175 0 : PR_DestroyCondVar(rwlock->rw_writer_waitq);
176 0 : PR_DestroyLock(rwlock->rw_lock);
177 : #endif
178 0 : if (rwlock->rw_name != NULL)
179 0 : PR_Free(rwlock->rw_name);
180 0 : PR_DELETE(rwlock);
181 0 : }
182 :
183 : /*
184 : ** Read-lock the RWLock.
185 : */
186 : PR_IMPLEMENT(void)
187 : PR_RWLock_Rlock(PRRWLock *rwlock)
188 : {
189 : #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
190 : int err;
191 : #endif
192 :
193 : #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
194 : /*
195 : * assert that rank ordering is not violated; the rank of 'rwlock' should
196 : * be equal to or greater than the highest rank of all the locks held by
197 : * the thread.
198 : */
199 0 : PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) ||
200 : (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK()));
201 : #endif
202 :
203 : #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
204 : err = RWLOCK_RDLOCK(&rwlock->rw_lock);
205 : PR_ASSERT(err == 0);
206 : #else
207 0 : PR_Lock(rwlock->rw_lock);
208 : /*
209 : * wait if write-locked or if a writer is waiting; preference for writers
210 : */
211 0 : while ((rwlock->rw_lock_cnt < 0) ||
212 0 : (rwlock->rw_writer_cnt > 0)) {
213 0 : rwlock->rw_reader_cnt++;
214 0 : PR_WaitCondVar(rwlock->rw_reader_waitq, PR_INTERVAL_NO_TIMEOUT);
215 0 : rwlock->rw_reader_cnt--;
216 : }
217 : /*
218 : * Increment read-lock count
219 : */
220 0 : rwlock->rw_lock_cnt++;
221 :
222 0 : PR_Unlock(rwlock->rw_lock);
223 : #endif
224 :
225 : #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
226 : /*
227 : * update thread's lock rank
228 : */
229 0 : if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE)
230 0 : _PR_SET_THREAD_RWLOCK_RANK(rwlock);
231 : #endif
232 0 : }
233 :
234 : /*
235 : ** Write-lock the RWLock.
236 : */
237 : PR_IMPLEMENT(void)
238 : PR_RWLock_Wlock(PRRWLock *rwlock)
239 : {
240 : #if defined(DEBUG)
241 0 : PRThread *me = PR_GetCurrentThread();
242 : #endif
243 : #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
244 : int err;
245 : #endif
246 :
247 : #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
248 : /*
249 : * assert that rank ordering is not violated; the rank of 'rwlock' should
250 : * be equal to or greater than the highest rank of all the locks held by
251 : * the thread.
252 : */
253 0 : PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) ||
254 : (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK()));
255 : #endif
256 :
257 : #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
258 : err = RWLOCK_WRLOCK(&rwlock->rw_lock);
259 : PR_ASSERT(err == 0);
260 : #else
261 0 : PR_Lock(rwlock->rw_lock);
262 : /*
263 : * wait if read locked
264 : */
265 0 : while (rwlock->rw_lock_cnt != 0) {
266 0 : rwlock->rw_writer_cnt++;
267 0 : PR_WaitCondVar(rwlock->rw_writer_waitq, PR_INTERVAL_NO_TIMEOUT);
268 0 : rwlock->rw_writer_cnt--;
269 : }
270 : /*
271 : * apply write lock
272 : */
273 0 : rwlock->rw_lock_cnt--;
274 0 : PR_ASSERT(rwlock->rw_lock_cnt == -1);
275 : #ifdef DEBUG
276 0 : PR_ASSERT(me != NULL);
277 0 : rwlock->rw_owner = me;
278 : #endif
279 0 : PR_Unlock(rwlock->rw_lock);
280 : #endif
281 :
282 : #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
283 : /*
284 : * update thread's lock rank
285 : */
286 0 : if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE)
287 0 : _PR_SET_THREAD_RWLOCK_RANK(rwlock);
288 : #endif
289 0 : }
290 :
291 : /*
292 : ** Unlock the RW lock.
293 : */
294 : PR_IMPLEMENT(void)
295 : PR_RWLock_Unlock(PRRWLock *rwlock)
296 : {
297 : #if defined(DEBUG)
298 0 : PRThread *me = PR_GetCurrentThread();
299 : #endif
300 : #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
301 : int err;
302 : #endif
303 :
304 : #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
305 : err = RWLOCK_UNLOCK(&rwlock->rw_lock);
306 : PR_ASSERT(err == 0);
307 : #else
308 0 : PR_Lock(rwlock->rw_lock);
309 : /*
310 : * lock must be read or write-locked
311 : */
312 0 : PR_ASSERT(rwlock->rw_lock_cnt != 0);
313 0 : if (rwlock->rw_lock_cnt > 0) {
314 :
315 : /*
316 : * decrement read-lock count
317 : */
318 0 : rwlock->rw_lock_cnt--;
319 0 : if (rwlock->rw_lock_cnt == 0) {
320 : /*
321 : * lock is not read-locked anymore; wakeup a waiting writer
322 : */
323 0 : if (rwlock->rw_writer_cnt > 0)
324 0 : PR_NotifyCondVar(rwlock->rw_writer_waitq);
325 : }
326 : } else {
327 0 : PR_ASSERT(rwlock->rw_lock_cnt == -1);
328 :
329 0 : rwlock->rw_lock_cnt = 0;
330 : #ifdef DEBUG
331 0 : PR_ASSERT(rwlock->rw_owner == me);
332 0 : rwlock->rw_owner = NULL;
333 : #endif
334 : /*
335 : * wakeup a writer, if present; preference for writers
336 : */
337 0 : if (rwlock->rw_writer_cnt > 0)
338 0 : PR_NotifyCondVar(rwlock->rw_writer_waitq);
339 : /*
340 : * else, wakeup all readers, if any
341 : */
342 0 : else if (rwlock->rw_reader_cnt > 0)
343 0 : PR_NotifyAllCondVar(rwlock->rw_reader_waitq);
344 : }
345 0 : PR_Unlock(rwlock->rw_lock);
346 : #endif
347 :
348 : #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
349 : /*
350 : * update thread's lock rank
351 : */
352 0 : if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE)
353 0 : _PR_UNSET_THREAD_RWLOCK_RANK(rwlock);
354 : #endif
355 0 : return;
356 : }
357 :
358 : #ifndef _PR_RWLOCK_RANK_ORDER_DEBUG
359 :
360 : void _PR_InitRWLocks(void) { }
361 :
362 : #else
363 :
364 3 : void _PR_InitRWLocks(void)
365 : {
366 : /*
367 : * allocated thread-private-data index for rwlock list
368 : */
369 3 : if (PR_NewThreadPrivateIndex(&pr_thread_rwlock_key,
370 : _PR_RELEASE_LOCK_STACK) == PR_FAILURE) {
371 0 : pr_thread_rwlock_alloc_failed = 1;
372 0 : return;
373 : }
374 : }
375 :
376 : /*
377 : * _PR_SET_THREAD_RWLOCK_RANK
378 : * Set a thread's lock rank, which is the highest of the ranks of all
379 : * the locks held by the thread. Pointers to the locks are added to a
380 : * per-thread list, which is anchored off a thread-private data key.
381 : */
382 :
383 : static void
384 0 : _PR_SET_THREAD_RWLOCK_RANK(PRRWLock *rwlock)
385 : {
386 : thread_rwlock_stack *lock_stack;
387 : PRStatus rv;
388 :
389 : /*
390 : * allocate a lock stack
391 : */
392 0 : if ((lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key)) == NULL) {
393 0 : lock_stack = (thread_rwlock_stack *)
394 : PR_CALLOC(1 * sizeof(thread_rwlock_stack));
395 0 : if (lock_stack) {
396 0 : rv = PR_SetThreadPrivate(pr_thread_rwlock_key, lock_stack);
397 0 : if (rv == PR_FAILURE) {
398 0 : PR_DELETE(lock_stack);
399 0 : pr_thread_rwlock_alloc_failed = 1;
400 0 : return;
401 : }
402 : } else {
403 0 : pr_thread_rwlock_alloc_failed = 1;
404 0 : return;
405 : }
406 : }
407 : /*
408 : * add rwlock to lock stack, if limit is not exceeded
409 : */
410 0 : if (lock_stack) {
411 0 : if (lock_stack->trs_index < _PR_RWLOCK_RANK_ORDER_LIMIT)
412 0 : lock_stack->trs_stack[lock_stack->trs_index++] = rwlock;
413 : }
414 : }
415 :
416 : static void
417 0 : _PR_RELEASE_LOCK_STACK(void *lock_stack)
418 : {
419 0 : PR_ASSERT(lock_stack);
420 0 : PR_DELETE(lock_stack);
421 0 : }
422 :
423 : /*
424 : * _PR_GET_THREAD_RWLOCK_RANK
425 : *
426 : * return thread's lock rank. If thread-private-data for the lock
427 : * stack is not allocated, return PR_RWLOCK_RANK_NONE.
428 : */
429 :
430 : static PRUint32
431 0 : _PR_GET_THREAD_RWLOCK_RANK(void)
432 : {
433 : thread_rwlock_stack *lock_stack;
434 :
435 0 : lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key);
436 0 : if (lock_stack == NULL || lock_stack->trs_index == 0)
437 0 : return (PR_RWLOCK_RANK_NONE);
438 : else
439 0 : return(lock_stack->trs_stack[lock_stack->trs_index - 1]->rw_rank);
440 : }
441 :
442 : /*
443 : * _PR_UNSET_THREAD_RWLOCK_RANK
444 : *
445 : * remove the rwlock from the lock stack. Since locks may not be
446 : * unlocked in a FIFO order, the entire lock stack is searched.
447 : */
448 :
449 : static void
450 0 : _PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock *rwlock)
451 : {
452 : thread_rwlock_stack *lock_stack;
453 0 : int new_index = 0, index, done = 0;
454 :
455 0 : lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key);
456 :
457 0 : PR_ASSERT(lock_stack != NULL);
458 :
459 0 : for (index = lock_stack->trs_index - 1; index >= 0; index--) {
460 0 : if (!done && (lock_stack->trs_stack[index] == rwlock)) {
461 : /*
462 : * reset the slot for rwlock
463 : */
464 0 : lock_stack->trs_stack[index] = NULL;
465 0 : done = 1;
466 : }
467 : /*
468 : * search for the lowest-numbered empty slot, above which there are
469 : * no non-empty slots
470 : */
471 0 : if (!new_index && (lock_stack->trs_stack[index] != NULL))
472 0 : new_index = index + 1;
473 0 : if (done && new_index)
474 0 : break;
475 : }
476 : /*
477 : * set top of stack to highest numbered empty slot
478 : */
479 0 : lock_stack->trs_index = new_index;
480 :
481 0 : }
482 :
483 : #endif /* _PR_RWLOCK_RANK_ORDER_DEBUG */
|