Coverage Report

Created: 2024-02-25 06:31

/src/nspr/pr/src/threads/prrwlock.c
Line
Count
Source (jump to first uncovered line)
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)
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
0
#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
245
{
99
245
    PRRWLock *rwlock;
100
#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
101
    int err;
102
#endif
103
104
245
    if (!_pr_initialized) {
105
0
        _PR_ImplicitInitialization();
106
0
    }
107
108
245
    rwlock = PR_NEWZAP(PRRWLock);
109
245
    if (rwlock == NULL) {
110
0
        return NULL;
111
0
    }
112
113
245
    rwlock->rw_rank = lock_rank;
114
245
    if (lock_name != NULL) {
115
0
        rwlock->rw_name = (char*) PR_Malloc(strlen(lock_name) + 1);
116
0
        if (rwlock->rw_name == NULL) {
117
0
            PR_DELETE(rwlock);
118
0
            return(NULL);
119
0
        }
120
0
        strcpy(rwlock->rw_name, lock_name);
121
245
    } else {
122
245
        rwlock->rw_name = NULL;
123
245
    }
124
125
#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
126
    err = RWLOCK_INIT(&rwlock->rw_lock);
127
    if (err != 0) {
128
        PR_SetError(PR_UNKNOWN_ERROR, err);
129
        PR_Free(rwlock->rw_name);
130
        PR_DELETE(rwlock);
131
        return NULL;
132
    }
133
    return rwlock;
134
#else
135
245
    rwlock->rw_lock = PR_NewLock();
136
245
    if (rwlock->rw_lock == NULL) {
137
0
        goto failed;
138
0
    }
139
245
    rwlock->rw_reader_waitq = PR_NewCondVar(rwlock->rw_lock);
140
245
    if (rwlock->rw_reader_waitq == NULL) {
141
0
        goto failed;
142
0
    }
143
245
    rwlock->rw_writer_waitq = PR_NewCondVar(rwlock->rw_lock);
144
245
    if (rwlock->rw_writer_waitq == NULL) {
145
0
        goto failed;
146
0
    }
147
245
    rwlock->rw_reader_cnt = 0;
148
245
    rwlock->rw_writer_cnt = 0;
149
245
    rwlock->rw_lock_cnt = 0;
150
245
    return rwlock;
151
152
0
failed:
153
0
    if (rwlock->rw_reader_waitq != NULL) {
154
0
        PR_DestroyCondVar(rwlock->rw_reader_waitq);
155
0
    }
156
0
    if (rwlock->rw_lock != NULL) {
157
0
        PR_DestroyLock(rwlock->rw_lock);
158
0
    }
159
0
    PR_Free(rwlock->rw_name);
160
0
    PR_DELETE(rwlock);
161
0
    return NULL;
162
245
#endif
163
245
}
164
165
/*
166
** Destroy the given RWLock "lock".
167
*/
168
PR_IMPLEMENT(void)
169
PR_DestroyRWLock(PRRWLock *rwlock)
170
245
{
171
#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
172
    int err;
173
    err = RWLOCK_DESTROY(&rwlock->rw_lock);
174
    PR_ASSERT(err == 0);
175
#else
176
245
    PR_ASSERT(rwlock->rw_reader_cnt == 0);
177
245
    PR_DestroyCondVar(rwlock->rw_reader_waitq);
178
245
    PR_DestroyCondVar(rwlock->rw_writer_waitq);
179
245
    PR_DestroyLock(rwlock->rw_lock);
180
245
#endif
181
245
    if (rwlock->rw_name != NULL) {
182
0
        PR_Free(rwlock->rw_name);
183
0
    }
184
245
    PR_DELETE(rwlock);
185
245
}
186
187
/*
188
** Read-lock the RWLock.
189
*/
190
PR_IMPLEMENT(void)
191
PR_RWLock_Rlock(PRRWLock *rwlock)
192
6
{
193
#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
194
    int err;
195
#endif
196
197
6
#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
198
    /*
199
     * assert that rank ordering is not violated; the rank of 'rwlock' should
200
     * be equal to or greater than the highest rank of all the locks held by
201
     * the thread.
202
     */
203
6
    PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) ||
204
6
              (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK()));
205
6
#endif
206
207
#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
208
    err = RWLOCK_RDLOCK(&rwlock->rw_lock);
209
    PR_ASSERT(err == 0);
210
#else
211
6
    PR_Lock(rwlock->rw_lock);
212
    /*
213
     * wait if write-locked or if a writer is waiting; preference for writers
214
     */
215
6
    while ((rwlock->rw_lock_cnt < 0) ||
216
6
           (rwlock->rw_writer_cnt > 0)) {
217
0
        rwlock->rw_reader_cnt++;
218
0
        PR_WaitCondVar(rwlock->rw_reader_waitq, PR_INTERVAL_NO_TIMEOUT);
219
0
        rwlock->rw_reader_cnt--;
220
0
    }
221
    /*
222
     * Increment read-lock count
223
     */
224
6
    rwlock->rw_lock_cnt++;
225
226
6
    PR_Unlock(rwlock->rw_lock);
227
6
#endif
228
229
6
#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
230
    /*
231
     * update thread's lock rank
232
     */
233
6
    if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) {
234
0
        _PR_SET_THREAD_RWLOCK_RANK(rwlock);
235
0
    }
236
6
#endif
237
6
}
238
239
/*
240
** Write-lock the RWLock.
241
*/
242
PR_IMPLEMENT(void)
243
PR_RWLock_Wlock(PRRWLock *rwlock)
244
4
{
245
4
#if defined(DEBUG)
246
4
    PRThread *me = PR_GetCurrentThread();
247
4
#endif
248
#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
249
    int err;
250
#endif
251
252
4
#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
253
    /*
254
     * assert that rank ordering is not violated; the rank of 'rwlock' should
255
     * be equal to or greater than the highest rank of all the locks held by
256
     * the thread.
257
     */
258
4
    PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) ||
259
4
              (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK()));
260
4
#endif
261
262
#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
263
    err = RWLOCK_WRLOCK(&rwlock->rw_lock);
264
    PR_ASSERT(err == 0);
265
#else
266
4
    PR_Lock(rwlock->rw_lock);
267
    /*
268
     * wait if read locked
269
     */
270
4
    while (rwlock->rw_lock_cnt != 0) {
271
0
        rwlock->rw_writer_cnt++;
272
0
        PR_WaitCondVar(rwlock->rw_writer_waitq, PR_INTERVAL_NO_TIMEOUT);
273
0
        rwlock->rw_writer_cnt--;
274
0
    }
275
    /*
276
     * apply write lock
277
     */
278
4
    rwlock->rw_lock_cnt--;
279
4
    PR_ASSERT(rwlock->rw_lock_cnt == -1);
280
4
#ifdef DEBUG
281
4
    PR_ASSERT(me != NULL);
282
4
    rwlock->rw_owner = me;
283
4
#endif
284
4
    PR_Unlock(rwlock->rw_lock);
285
4
#endif
286
287
4
#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
288
    /*
289
     * update thread's lock rank
290
     */
291
4
    if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) {
292
0
        _PR_SET_THREAD_RWLOCK_RANK(rwlock);
293
0
    }
294
4
#endif
295
4
}
296
297
/*
298
** Unlock the RW lock.
299
*/
300
PR_IMPLEMENT(void)
301
PR_RWLock_Unlock(PRRWLock *rwlock)
302
10
{
303
10
#if defined(DEBUG)
304
10
    PRThread *me = PR_GetCurrentThread();
305
10
#endif
306
#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
307
    int err;
308
#endif
309
310
#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
311
    err = RWLOCK_UNLOCK(&rwlock->rw_lock);
312
    PR_ASSERT(err == 0);
313
#else
314
10
    PR_Lock(rwlock->rw_lock);
315
    /*
316
     * lock must be read or write-locked
317
     */
318
10
    PR_ASSERT(rwlock->rw_lock_cnt != 0);
319
10
    if (rwlock->rw_lock_cnt > 0) {
320
321
        /*
322
         * decrement read-lock count
323
         */
324
6
        rwlock->rw_lock_cnt--;
325
6
        if (rwlock->rw_lock_cnt == 0) {
326
            /*
327
             * lock is not read-locked anymore; wakeup a waiting writer
328
             */
329
6
            if (rwlock->rw_writer_cnt > 0) {
330
0
                PR_NotifyCondVar(rwlock->rw_writer_waitq);
331
0
            }
332
6
        }
333
6
    } else {
334
4
        PR_ASSERT(rwlock->rw_lock_cnt == -1);
335
336
4
        rwlock->rw_lock_cnt = 0;
337
4
#ifdef DEBUG
338
4
        PR_ASSERT(rwlock->rw_owner == me);
339
4
        rwlock->rw_owner = NULL;
340
4
#endif
341
        /*
342
         * wakeup a writer, if present; preference for writers
343
         */
344
4
        if (rwlock->rw_writer_cnt > 0) {
345
0
            PR_NotifyCondVar(rwlock->rw_writer_waitq);
346
0
        }
347
        /*
348
         * else, wakeup all readers, if any
349
         */
350
4
        else if (rwlock->rw_reader_cnt > 0) {
351
0
            PR_NotifyAllCondVar(rwlock->rw_reader_waitq);
352
0
        }
353
4
    }
354
10
    PR_Unlock(rwlock->rw_lock);
355
10
#endif
356
357
10
#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
358
    /*
359
     * update thread's lock rank
360
     */
361
10
    if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) {
362
0
        _PR_UNSET_THREAD_RWLOCK_RANK(rwlock);
363
0
    }
364
10
#endif
365
10
    return;
366
10
}
367
368
#ifndef _PR_RWLOCK_RANK_ORDER_DEBUG
369
370
void _PR_InitRWLocks(void) { }
371
372
#else
373
374
void _PR_InitRWLocks(void)
375
10
{
376
    /*
377
     * allocated thread-private-data index for rwlock list
378
     */
379
10
    if (PR_NewThreadPrivateIndex(&pr_thread_rwlock_key,
380
10
                                 _PR_RELEASE_LOCK_STACK) == PR_FAILURE) {
381
0
        pr_thread_rwlock_alloc_failed = 1;
382
0
        return;
383
0
    }
384
10
}
385
386
/*
387
 * _PR_SET_THREAD_RWLOCK_RANK
388
 *      Set a thread's lock rank, which is the highest of the ranks of all
389
 *      the locks held by the thread. Pointers to the locks are added to a
390
 *      per-thread list, which is anchored off a thread-private data key.
391
 */
392
393
static void
394
_PR_SET_THREAD_RWLOCK_RANK(PRRWLock *rwlock)
395
0
{
396
0
    thread_rwlock_stack *lock_stack;
397
0
    PRStatus rv;
398
399
    /*
400
     * allocate a lock stack
401
     */
402
0
    if ((lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key)) == NULL) {
403
0
        lock_stack = (thread_rwlock_stack *)
404
0
                     PR_CALLOC(1 * sizeof(thread_rwlock_stack));
405
0
        if (lock_stack) {
406
0
            rv = PR_SetThreadPrivate(pr_thread_rwlock_key, lock_stack);
407
0
            if (rv == PR_FAILURE) {
408
0
                PR_DELETE(lock_stack);
409
0
                pr_thread_rwlock_alloc_failed = 1;
410
0
                return;
411
0
            }
412
0
        } else {
413
0
            pr_thread_rwlock_alloc_failed = 1;
414
0
            return;
415
0
        }
416
0
    }
417
    /*
418
     * add rwlock to lock stack, if limit is not exceeded
419
     */
420
0
    if (lock_stack) {
421
0
        if (lock_stack->trs_index < _PR_RWLOCK_RANK_ORDER_LIMIT) {
422
0
            lock_stack->trs_stack[lock_stack->trs_index++] = rwlock;
423
0
        }
424
0
    }
425
0
}
426
427
static void
428
_PR_RELEASE_LOCK_STACK(void *lock_stack)
429
0
{
430
0
    PR_ASSERT(lock_stack);
431
0
    PR_DELETE(lock_stack);
432
0
}
433
434
/*
435
 * _PR_GET_THREAD_RWLOCK_RANK
436
 *
437
 *      return thread's lock rank. If thread-private-data for the lock
438
 *      stack is not allocated, return PR_RWLOCK_RANK_NONE.
439
 */
440
441
static PRUint32
442
_PR_GET_THREAD_RWLOCK_RANK(void)
443
0
{
444
0
    thread_rwlock_stack *lock_stack;
445
446
0
    lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key);
447
0
    if (lock_stack == NULL || lock_stack->trs_index == 0) {
448
0
        return (PR_RWLOCK_RANK_NONE);
449
0
    }
450
0
    else {
451
0
        return(lock_stack->trs_stack[lock_stack->trs_index - 1]->rw_rank);
452
0
    }
453
0
}
454
455
/*
456
 * _PR_UNSET_THREAD_RWLOCK_RANK
457
 *
458
 *      remove the rwlock from the lock stack. Since locks may not be
459
 *      unlocked in a FIFO order, the entire lock stack is searched.
460
 */
461
462
static void
463
_PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock *rwlock)
464
0
{
465
0
    thread_rwlock_stack *lock_stack;
466
0
    int new_index = 0, index, done = 0;
467
468
0
    lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key);
469
470
0
    PR_ASSERT(lock_stack != NULL);
471
472
0
    for (index = lock_stack->trs_index - 1; index >= 0; index--) {
473
0
        if (!done && (lock_stack->trs_stack[index] == rwlock))  {
474
            /*
475
             * reset the slot for rwlock
476
             */
477
0
            lock_stack->trs_stack[index] = NULL;
478
0
            done = 1;
479
0
        }
480
        /*
481
         * search for the lowest-numbered empty slot, above which there are
482
         * no non-empty slots
483
         */
484
0
        if (!new_index && (lock_stack->trs_stack[index] != NULL)) {
485
0
            new_index = index + 1;
486
0
        }
487
0
        if (done && new_index) {
488
0
            break;
489
0
        }
490
0
    }
491
    /*
492
     * set top of stack to highest numbered empty slot
493
     */
494
0
    lock_stack->trs_index = new_index;
495
496
0
}
497
498
#endif  /* _PR_RWLOCK_RANK_ORDER_DEBUG */