1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is the Netscape Portable Runtime (NSPR).
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : /*
39 : ** PR Atomic operations
40 : */
41 :
42 :
43 : #include "pratom.h"
44 : #include "primpl.h"
45 :
46 : #include <string.h>
47 :
48 : /*
49 : * The following is a fallback implementation that emulates
50 : * atomic operations for platforms without atomic operations.
51 : * If a platform has atomic operations, it should define the
52 : * macro _PR_HAVE_ATOMIC_OPS, and the following will not be
53 : * compiled in.
54 : */
55 :
56 : #if !defined(_PR_HAVE_ATOMIC_OPS)
57 :
58 : #if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
59 : /*
60 : * PR_AtomicDecrement() is used in NSPR's thread-specific data
61 : * destructor. Because thread-specific data destructors may be
62 : * invoked after a PR_Cleanup() call, we need an implementation
63 : * of the atomic routines that doesn't need NSPR to be initialized.
64 : */
65 :
66 : /*
67 : * We use a set of locks for all the emulated atomic operations.
68 : * By hashing on the address of the integer to be locked the
69 : * contention between multiple threads should be lessened.
70 : *
71 : * The number of atomic locks can be set by the environment variable
72 : * NSPR_ATOMIC_HASH_LOCKS
73 : */
74 :
75 : /*
76 : * lock counts should be a power of 2
77 : */
78 : #define DEFAULT_ATOMIC_LOCKS 16 /* should be in sync with the number of initializers
79 : below */
80 : #define MAX_ATOMIC_LOCKS (4 * 1024)
81 :
82 : static pthread_mutex_t static_atomic_locks[DEFAULT_ATOMIC_LOCKS] = {
83 : PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
84 : PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
85 : PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
86 : PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
87 : PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
88 : PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
89 : PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
90 : PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER };
91 :
92 : #ifdef DEBUG
93 : static PRInt32 static_hash_lock_counts[DEFAULT_ATOMIC_LOCKS];
94 : static PRInt32 *hash_lock_counts = static_hash_lock_counts;
95 : #endif
96 :
97 : static PRUint32 num_atomic_locks = DEFAULT_ATOMIC_LOCKS;
98 : static pthread_mutex_t *atomic_locks = static_atomic_locks;
99 : static PRUint32 atomic_hash_mask = DEFAULT_ATOMIC_LOCKS - 1;
100 :
101 : #define _PR_HASH_FOR_LOCK(ptr) \
102 : ((PRUint32) (((PRUptrdiff) (ptr) >> 2) ^ \
103 : ((PRUptrdiff) (ptr) >> 8)) & \
104 : atomic_hash_mask)
105 :
106 : void _PR_MD_INIT_ATOMIC()
107 : {
108 : char *eval;
109 : int index;
110 :
111 :
112 : PR_ASSERT(PR_FloorLog2(MAX_ATOMIC_LOCKS) ==
113 : PR_CeilingLog2(MAX_ATOMIC_LOCKS));
114 :
115 : PR_ASSERT(PR_FloorLog2(DEFAULT_ATOMIC_LOCKS) ==
116 : PR_CeilingLog2(DEFAULT_ATOMIC_LOCKS));
117 :
118 : if (((eval = getenv("NSPR_ATOMIC_HASH_LOCKS")) != NULL) &&
119 : ((num_atomic_locks = atoi(eval)) != DEFAULT_ATOMIC_LOCKS)) {
120 :
121 : if (num_atomic_locks > MAX_ATOMIC_LOCKS)
122 : num_atomic_locks = MAX_ATOMIC_LOCKS;
123 : else if (num_atomic_locks < 1)
124 : num_atomic_locks = 1;
125 : else {
126 : num_atomic_locks = PR_FloorLog2(num_atomic_locks);
127 : num_atomic_locks = 1L << num_atomic_locks;
128 : }
129 : atomic_locks = (pthread_mutex_t *) PR_Malloc(sizeof(pthread_mutex_t) *
130 : num_atomic_locks);
131 : if (atomic_locks) {
132 : for (index = 0; index < num_atomic_locks; index++) {
133 : if (pthread_mutex_init(&atomic_locks[index], NULL)) {
134 : PR_DELETE(atomic_locks);
135 : atomic_locks = NULL;
136 : break;
137 : }
138 : }
139 : }
140 : #ifdef DEBUG
141 : if (atomic_locks) {
142 : hash_lock_counts = PR_CALLOC(num_atomic_locks * sizeof(PRInt32));
143 : if (hash_lock_counts == NULL) {
144 : PR_DELETE(atomic_locks);
145 : atomic_locks = NULL;
146 : }
147 : }
148 : #endif
149 : if (atomic_locks == NULL) {
150 : /*
151 : * Use statically allocated locks
152 : */
153 : atomic_locks = static_atomic_locks;
154 : num_atomic_locks = DEFAULT_ATOMIC_LOCKS;
155 : #ifdef DEBUG
156 : hash_lock_counts = static_hash_lock_counts;
157 : #endif
158 : }
159 : atomic_hash_mask = num_atomic_locks - 1;
160 : }
161 : PR_ASSERT(PR_FloorLog2(num_atomic_locks) ==
162 : PR_CeilingLog2(num_atomic_locks));
163 : }
164 :
165 : PRInt32
166 : _PR_MD_ATOMIC_INCREMENT(PRInt32 *val)
167 : {
168 : PRInt32 rv;
169 : PRInt32 idx = _PR_HASH_FOR_LOCK(val);
170 :
171 : pthread_mutex_lock(&atomic_locks[idx]);
172 : rv = ++(*val);
173 : #ifdef DEBUG
174 : hash_lock_counts[idx]++;
175 : #endif
176 : pthread_mutex_unlock(&atomic_locks[idx]);
177 : return rv;
178 : }
179 :
180 : PRInt32
181 : _PR_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val)
182 : {
183 : PRInt32 rv;
184 : PRInt32 idx = _PR_HASH_FOR_LOCK(ptr);
185 :
186 : pthread_mutex_lock(&atomic_locks[idx]);
187 : rv = ((*ptr) += val);
188 : #ifdef DEBUG
189 : hash_lock_counts[idx]++;
190 : #endif
191 : pthread_mutex_unlock(&atomic_locks[idx]);
192 : return rv;
193 : }
194 :
195 : PRInt32
196 : _PR_MD_ATOMIC_DECREMENT(PRInt32 *val)
197 : {
198 : PRInt32 rv;
199 : PRInt32 idx = _PR_HASH_FOR_LOCK(val);
200 :
201 : pthread_mutex_lock(&atomic_locks[idx]);
202 : rv = --(*val);
203 : #ifdef DEBUG
204 : hash_lock_counts[idx]++;
205 : #endif
206 : pthread_mutex_unlock(&atomic_locks[idx]);
207 : return rv;
208 : }
209 :
210 : PRInt32
211 : _PR_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
212 : {
213 : PRInt32 rv;
214 : PRInt32 idx = _PR_HASH_FOR_LOCK(val);
215 :
216 : pthread_mutex_lock(&atomic_locks[idx]);
217 : rv = *val;
218 : *val = newval;
219 : #ifdef DEBUG
220 : hash_lock_counts[idx]++;
221 : #endif
222 : pthread_mutex_unlock(&atomic_locks[idx]);
223 : return rv;
224 : }
225 : #else /* _PR_PTHREADS && !_PR_DCETHREADS */
226 : /*
227 : * We use a single lock for all the emulated atomic operations.
228 : * The lock contention should be acceptable.
229 : */
230 : static PRLock *atomic_lock = NULL;
231 : void _PR_MD_INIT_ATOMIC(void)
232 : {
233 : if (atomic_lock == NULL) {
234 : atomic_lock = PR_NewLock();
235 : }
236 : }
237 :
238 : PRInt32
239 : _PR_MD_ATOMIC_INCREMENT(PRInt32 *val)
240 : {
241 : PRInt32 rv;
242 :
243 : if (!_pr_initialized) {
244 : _PR_ImplicitInitialization();
245 : }
246 : PR_Lock(atomic_lock);
247 : rv = ++(*val);
248 : PR_Unlock(atomic_lock);
249 : return rv;
250 : }
251 :
252 : PRInt32
253 : _PR_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val)
254 : {
255 : PRInt32 rv;
256 :
257 : if (!_pr_initialized) {
258 : _PR_ImplicitInitialization();
259 : }
260 : PR_Lock(atomic_lock);
261 : rv = ((*ptr) += val);
262 : PR_Unlock(atomic_lock);
263 : return rv;
264 : }
265 :
266 : PRInt32
267 : _PR_MD_ATOMIC_DECREMENT(PRInt32 *val)
268 : {
269 : PRInt32 rv;
270 :
271 : if (!_pr_initialized) {
272 : _PR_ImplicitInitialization();
273 : }
274 : PR_Lock(atomic_lock);
275 : rv = --(*val);
276 : PR_Unlock(atomic_lock);
277 : return rv;
278 : }
279 :
280 : PRInt32
281 : _PR_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
282 : {
283 : PRInt32 rv;
284 :
285 : if (!_pr_initialized) {
286 : _PR_ImplicitInitialization();
287 : }
288 : PR_Lock(atomic_lock);
289 : rv = *val;
290 : *val = newval;
291 : PR_Unlock(atomic_lock);
292 : return rv;
293 : }
294 : #endif /* _PR_PTHREADS && !_PR_DCETHREADS */
295 :
296 : #endif /* !_PR_HAVE_ATOMIC_OPS */
297 :
298 20034 : void _PR_InitAtomic(void)
299 : {
300 : _PR_MD_INIT_ATOMIC();
301 20034 : }
302 :
303 : PR_IMPLEMENT(PRInt32)
304 0 : PR_AtomicIncrement(PRInt32 *val)
305 : {
306 0 : return _PR_MD_ATOMIC_INCREMENT(val);
307 : }
308 :
309 : PR_IMPLEMENT(PRInt32)
310 0 : PR_AtomicDecrement(PRInt32 *val)
311 : {
312 0 : return _PR_MD_ATOMIC_DECREMENT(val);
313 : }
314 :
315 : PR_IMPLEMENT(PRInt32)
316 984 : PR_AtomicSet(PRInt32 *val, PRInt32 newval)
317 : {
318 984 : return _PR_MD_ATOMIC_SET(val, newval);
319 : }
320 :
321 : PR_IMPLEMENT(PRInt32)
322 8552 : PR_AtomicAdd(PRInt32 *ptr, PRInt32 val)
323 : {
324 8552 : return _PR_MD_ATOMIC_ADD(ptr, val);
325 : }
326 : /*
327 : * For platforms, which don't support the CAS (compare-and-swap) instruction
328 : * (or an equivalent), the stack operations are implemented by use of PRLock
329 : */
330 :
331 : PR_IMPLEMENT(PRStack *)
332 20034 : PR_CreateStack(const char *stack_name)
333 : {
334 : PRStack *stack;
335 :
336 20034 : if (!_pr_initialized) {
337 0 : _PR_ImplicitInitialization();
338 : }
339 :
340 20034 : if ((stack = PR_NEW(PRStack)) == NULL) {
341 0 : return NULL;
342 : }
343 20034 : if (stack_name) {
344 20034 : stack->prstk_name = (char *) PR_Malloc(strlen(stack_name) + 1);
345 20034 : if (stack->prstk_name == NULL) {
346 0 : PR_DELETE(stack);
347 0 : return NULL;
348 : }
349 20034 : strcpy(stack->prstk_name, stack_name);
350 : } else
351 0 : stack->prstk_name = NULL;
352 :
353 : #ifndef _PR_HAVE_ATOMIC_CAS
354 20034 : stack->prstk_lock = PR_NewLock();
355 20034 : if (stack->prstk_lock == NULL) {
356 0 : PR_Free(stack->prstk_name);
357 0 : PR_DELETE(stack);
358 0 : return NULL;
359 : }
360 : #endif /* !_PR_HAVE_ATOMIC_CAS */
361 :
362 20034 : stack->prstk_head.prstk_elem_next = NULL;
363 :
364 20034 : return stack;
365 : }
366 :
367 : PR_IMPLEMENT(PRStatus)
368 140 : PR_DestroyStack(PRStack *stack)
369 : {
370 140 : if (stack->prstk_head.prstk_elem_next != NULL) {
371 0 : PR_SetError(PR_INVALID_STATE_ERROR, 0);
372 0 : return PR_FAILURE;
373 : }
374 :
375 140 : if (stack->prstk_name)
376 140 : PR_Free(stack->prstk_name);
377 : #ifndef _PR_HAVE_ATOMIC_CAS
378 140 : PR_DestroyLock(stack->prstk_lock);
379 : #endif /* !_PR_HAVE_ATOMIC_CAS */
380 140 : PR_DELETE(stack);
381 :
382 140 : return PR_SUCCESS;
383 : }
384 :
385 : #ifndef _PR_HAVE_ATOMIC_CAS
386 :
387 : PR_IMPLEMENT(void)
388 0 : PR_StackPush(PRStack *stack, PRStackElem *stack_elem)
389 : {
390 0 : PR_Lock(stack->prstk_lock);
391 0 : stack_elem->prstk_elem_next = stack->prstk_head.prstk_elem_next;
392 0 : stack->prstk_head.prstk_elem_next = stack_elem;
393 0 : PR_Unlock(stack->prstk_lock);
394 : return;
395 : }
396 :
397 : PR_IMPLEMENT(PRStackElem *)
398 140 : PR_StackPop(PRStack *stack)
399 : {
400 : PRStackElem *element;
401 :
402 140 : PR_Lock(stack->prstk_lock);
403 140 : element = stack->prstk_head.prstk_elem_next;
404 140 : if (element != NULL) {
405 0 : stack->prstk_head.prstk_elem_next = element->prstk_elem_next;
406 0 : element->prstk_elem_next = NULL; /* debugging aid */
407 : }
408 140 : PR_Unlock(stack->prstk_lock);
409 140 : return element;
410 : }
411 : #endif /* !_PR_HAVE_ATOMIC_CAS */
|