Coverage Report

Created: 2026-01-22 06:19

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