/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 */ |