Coverage Report

Created: 2025-07-01 06:26

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