Coverage Report

Created: 2026-08-18 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nspr/pr/src/misc/pratom.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
/*
6
**     PR Atomic operations
7
*/
8
9
#include "pratom.h"
10
#include "primpl.h"
11
12
#include <string.h>
13
14
/*
15
 * The following is a fallback implementation that emulates
16
 * atomic operations for platforms without atomic operations.
17
 * If a platform has atomic operations, it should define the
18
 * macro _PR_HAVE_ATOMIC_OPS, and the following will not be
19
 * compiled in.
20
 */
21
22
#if !defined(_PR_HAVE_ATOMIC_OPS)
23
24
#  if defined(_PR_PTHREADS)
25
/*
26
 * PR_AtomicDecrement() is used in NSPR's thread-specific data
27
 * destructor.  Because thread-specific data destructors may be
28
 * invoked after a PR_Cleanup() call, we need an implementation
29
 * of the atomic routines that doesn't need NSPR to be initialized.
30
 */
31
32
/*
33
 * We use a set of locks for all the emulated atomic operations.
34
 * By hashing on the address of the integer to be locked the
35
 * contention between multiple threads should be lessened.
36
 *
37
 * The number of atomic locks can be set by the environment variable
38
 * NSPR_ATOMIC_HASH_LOCKS
39
 */
40
41
/*
42
 * lock counts should be a power of 2
43
 */
44
#    define DEFAULT_ATOMIC_LOCKS                              \
45
      16 /* should be in sync with the number of initializers \
46
             below */
47
#    define MAX_ATOMIC_LOCKS (4 * 1024)
48
49
static pthread_mutex_t static_atomic_locks[DEFAULT_ATOMIC_LOCKS] = {
50
    PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
51
    PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
52
    PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
53
    PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
54
    PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
55
    PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
56
    PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
57
    PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER};
58
59
#    ifdef DEBUG
60
static PRInt32 static_hash_lock_counts[DEFAULT_ATOMIC_LOCKS];
61
static PRInt32* hash_lock_counts = static_hash_lock_counts;
62
#    endif
63
64
static PRUint32 num_atomic_locks = DEFAULT_ATOMIC_LOCKS;
65
static pthread_mutex_t* atomic_locks = static_atomic_locks;
66
static PRUint32 atomic_hash_mask = DEFAULT_ATOMIC_LOCKS - 1;
67
68
#    define _PR_HASH_FOR_LOCK(ptr)                                       \
69
      ((PRUint32)(((PRUptrdiff)(ptr) >> 2) ^ ((PRUptrdiff)(ptr) >> 8)) & \
70
       atomic_hash_mask)
71
72
void _PR_MD_INIT_ATOMIC() {
73
  char* eval;
74
  int index;
75
76
  PR_ASSERT(PR_FloorLog2(MAX_ATOMIC_LOCKS) == PR_CeilingLog2(MAX_ATOMIC_LOCKS));
77
78
  PR_ASSERT(PR_FloorLog2(DEFAULT_ATOMIC_LOCKS) ==
79
            PR_CeilingLog2(DEFAULT_ATOMIC_LOCKS));
80
81
  if (((eval = getenv("NSPR_ATOMIC_HASH_LOCKS")) != NULL) &&
82
      ((num_atomic_locks = atoi(eval)) != DEFAULT_ATOMIC_LOCKS)) {
83
    if (num_atomic_locks > MAX_ATOMIC_LOCKS) {
84
      num_atomic_locks = MAX_ATOMIC_LOCKS;
85
    } else if (num_atomic_locks < 1) {
86
      num_atomic_locks = 1;
87
    } else {
88
      num_atomic_locks = PR_FloorLog2(num_atomic_locks);
89
      num_atomic_locks = 1L << num_atomic_locks;
90
    }
91
    atomic_locks =
92
        (pthread_mutex_t*)PR_Malloc(sizeof(pthread_mutex_t) * num_atomic_locks);
93
    if (atomic_locks) {
94
      for (index = 0; index < num_atomic_locks; index++) {
95
        if (pthread_mutex_init(&atomic_locks[index], NULL)) {
96
          PR_DELETE(atomic_locks);
97
          atomic_locks = NULL;
98
          break;
99
        }
100
      }
101
    }
102
#    ifdef DEBUG
103
    if (atomic_locks) {
104
      hash_lock_counts = PR_CALLOC(num_atomic_locks * sizeof(PRInt32));
105
      if (hash_lock_counts == NULL) {
106
        PR_DELETE(atomic_locks);
107
        atomic_locks = NULL;
108
      }
109
    }
110
#    endif
111
    if (atomic_locks == NULL) {
112
      /*
113
       *  Use statically allocated locks
114
       */
115
      atomic_locks = static_atomic_locks;
116
      num_atomic_locks = DEFAULT_ATOMIC_LOCKS;
117
#    ifdef DEBUG
118
      hash_lock_counts = static_hash_lock_counts;
119
#    endif
120
    }
121
    atomic_hash_mask = num_atomic_locks - 1;
122
  }
123
  PR_ASSERT(PR_FloorLog2(num_atomic_locks) == PR_CeilingLog2(num_atomic_locks));
124
}
125
126
PRInt32 _PR_MD_ATOMIC_INCREMENT(PRInt32* val) {
127
  PRInt32 rv;
128
  PRInt32 idx = _PR_HASH_FOR_LOCK(val);
129
130
  pthread_mutex_lock(&atomic_locks[idx]);
131
  rv = ++(*val);
132
#    ifdef DEBUG
133
  hash_lock_counts[idx]++;
134
#    endif
135
  pthread_mutex_unlock(&atomic_locks[idx]);
136
  return rv;
137
}
138
139
PRInt32 _PR_MD_ATOMIC_ADD(PRInt32* ptr, PRInt32 val) {
140
  PRInt32 rv;
141
  PRInt32 idx = _PR_HASH_FOR_LOCK(ptr);
142
143
  pthread_mutex_lock(&atomic_locks[idx]);
144
  rv = ((*ptr) += val);
145
#    ifdef DEBUG
146
  hash_lock_counts[idx]++;
147
#    endif
148
  pthread_mutex_unlock(&atomic_locks[idx]);
149
  return rv;
150
}
151
152
PRInt32 _PR_MD_ATOMIC_DECREMENT(PRInt32* val) {
153
  PRInt32 rv;
154
  PRInt32 idx = _PR_HASH_FOR_LOCK(val);
155
156
  pthread_mutex_lock(&atomic_locks[idx]);
157
  rv = --(*val);
158
#    ifdef DEBUG
159
  hash_lock_counts[idx]++;
160
#    endif
161
  pthread_mutex_unlock(&atomic_locks[idx]);
162
  return rv;
163
}
164
165
PRInt32 _PR_MD_ATOMIC_SET(PRInt32* val, PRInt32 newval) {
166
  PRInt32 rv;
167
  PRInt32 idx = _PR_HASH_FOR_LOCK(val);
168
169
  pthread_mutex_lock(&atomic_locks[idx]);
170
  rv = *val;
171
  *val = newval;
172
#    ifdef DEBUG
173
  hash_lock_counts[idx]++;
174
#    endif
175
  pthread_mutex_unlock(&atomic_locks[idx]);
176
  return rv;
177
}
178
#  else  /* _PR_PTHREADS */
179
/*
180
 * We use a single lock for all the emulated atomic operations.
181
 * The lock contention should be acceptable.
182
 */
183
static PRLock* atomic_lock = NULL;
184
void _PR_MD_INIT_ATOMIC(void) {
185
  if (atomic_lock == NULL) {
186
    atomic_lock = PR_NewLock();
187
  }
188
}
189
190
PRInt32 _PR_MD_ATOMIC_INCREMENT(PRInt32* val) {
191
  PRInt32 rv;
192
193
  if (!_pr_initialized) {
194
    _PR_ImplicitInitialization();
195
  }
196
  PR_Lock(atomic_lock);
197
  rv = ++(*val);
198
  PR_Unlock(atomic_lock);
199
  return rv;
200
}
201
202
PRInt32 _PR_MD_ATOMIC_ADD(PRInt32* ptr, PRInt32 val) {
203
  PRInt32 rv;
204
205
  if (!_pr_initialized) {
206
    _PR_ImplicitInitialization();
207
  }
208
  PR_Lock(atomic_lock);
209
  rv = ((*ptr) += val);
210
  PR_Unlock(atomic_lock);
211
  return rv;
212
}
213
214
PRInt32 _PR_MD_ATOMIC_DECREMENT(PRInt32* val) {
215
  PRInt32 rv;
216
217
  if (!_pr_initialized) {
218
    _PR_ImplicitInitialization();
219
  }
220
  PR_Lock(atomic_lock);
221
  rv = --(*val);
222
  PR_Unlock(atomic_lock);
223
  return rv;
224
}
225
226
PRInt32 _PR_MD_ATOMIC_SET(PRInt32* val, PRInt32 newval) {
227
  PRInt32 rv;
228
229
  if (!_pr_initialized) {
230
    _PR_ImplicitInitialization();
231
  }
232
  PR_Lock(atomic_lock);
233
  rv = *val;
234
  *val = newval;
235
  PR_Unlock(atomic_lock);
236
  return rv;
237
}
238
#  endif /* _PR_PTHREADS */
239
240
#endif /* !_PR_HAVE_ATOMIC_OPS */
241
242
19
void _PR_InitAtomic(void) { _PR_MD_INIT_ATOMIC(); }
243
244
PR_IMPLEMENT(PRInt32)
245
0
PR_AtomicIncrement(PRInt32* val) { return _PR_MD_ATOMIC_INCREMENT(val); }
246
247
PR_IMPLEMENT(PRInt32)
248
0
PR_AtomicDecrement(PRInt32* val) { return _PR_MD_ATOMIC_DECREMENT(val); }
249
250
PR_IMPLEMENT(PRInt32)
251
0
PR_AtomicSet(PRInt32* val, PRInt32 newval) {
252
0
  return _PR_MD_ATOMIC_SET(val, newval);
253
0
}
254
255
PR_IMPLEMENT(PRInt32)
256
0
PR_AtomicAdd(PRInt32* ptr, PRInt32 val) { return _PR_MD_ATOMIC_ADD(ptr, val); }
257
/*
258
 * For platforms, which don't support the CAS (compare-and-swap) instruction
259
 * (or an equivalent), the stack operations are implemented by use of PRLock
260
 */
261
262
PR_IMPLEMENT(PRStack*)
263
0
PR_CreateStack(const char* stack_name) {
264
0
  PRStack* stack;
265
266
0
  if (!_pr_initialized) {
267
0
    _PR_ImplicitInitialization();
268
0
  }
269
270
0
  if ((stack = PR_NEW(PRStack)) == NULL) {
271
0
    return NULL;
272
0
  }
273
0
  if (stack_name) {
274
0
    stack->prstk_name = (char*)PR_Malloc(strlen(stack_name) + 1);
275
0
    if (stack->prstk_name == NULL) {
276
0
      PR_DELETE(stack);
277
0
      return NULL;
278
0
    }
279
0
    strcpy(stack->prstk_name, stack_name);
280
0
  } else {
281
0
    stack->prstk_name = NULL;
282
0
  }
283
284
0
#ifndef _PR_HAVE_ATOMIC_CAS
285
0
  stack->prstk_lock = PR_NewLock();
286
0
  if (stack->prstk_lock == NULL) {
287
0
    PR_Free(stack->prstk_name);
288
0
    PR_DELETE(stack);
289
0
    return NULL;
290
0
  }
291
0
#endif /* !_PR_HAVE_ATOMIC_CAS */
292
293
0
  stack->prstk_head.prstk_elem_next = NULL;
294
295
0
  return stack;
296
0
}
297
298
PR_IMPLEMENT(PRStatus)
299
0
PR_DestroyStack(PRStack* stack) {
300
0
  if (stack->prstk_head.prstk_elem_next != NULL) {
301
0
    PR_SetError(PR_INVALID_STATE_ERROR, 0);
302
0
    return PR_FAILURE;
303
0
  }
304
305
0
  if (stack->prstk_name) {
306
0
    PR_Free(stack->prstk_name);
307
0
  }
308
0
#ifndef _PR_HAVE_ATOMIC_CAS
309
0
  PR_DestroyLock(stack->prstk_lock);
310
0
#endif /* !_PR_HAVE_ATOMIC_CAS */
311
0
  PR_DELETE(stack);
312
313
0
  return PR_SUCCESS;
314
0
}
315
316
#ifndef _PR_HAVE_ATOMIC_CAS
317
318
PR_IMPLEMENT(void)
319
0
PR_StackPush(PRStack* stack, PRStackElem* stack_elem) {
320
0
  PR_Lock(stack->prstk_lock);
321
0
  stack_elem->prstk_elem_next = stack->prstk_head.prstk_elem_next;
322
0
  stack->prstk_head.prstk_elem_next = stack_elem;
323
0
  PR_Unlock(stack->prstk_lock);
324
0
  return;
325
0
}
326
327
PR_IMPLEMENT(PRStackElem*)
328
0
PR_StackPop(PRStack* stack) {
329
0
  PRStackElem* element;
330
331
0
  PR_Lock(stack->prstk_lock);
332
0
  element = stack->prstk_head.prstk_elem_next;
333
0
  if (element != NULL) {
334
0
    stack->prstk_head.prstk_elem_next = element->prstk_elem_next;
335
    element->prstk_elem_next = NULL; /* debugging aid */
336
0
  }
337
0
  PR_Unlock(stack->prstk_lock);
338
0
  return element;
339
0
}
340
#endif /* !_PR_HAVE_ATOMIC_CAS */