Coverage Report

Created: 2026-07-07 06:15

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