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