1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/nsprpub/pr/src/misc/pratom.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,379 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* 1.10 +** PR Atomic operations 1.11 +*/ 1.12 + 1.13 + 1.14 +#include "pratom.h" 1.15 +#include "primpl.h" 1.16 + 1.17 +#include <string.h> 1.18 + 1.19 +/* 1.20 + * The following is a fallback implementation that emulates 1.21 + * atomic operations for platforms without atomic operations. 1.22 + * If a platform has atomic operations, it should define the 1.23 + * macro _PR_HAVE_ATOMIC_OPS, and the following will not be 1.24 + * compiled in. 1.25 + */ 1.26 + 1.27 +#if !defined(_PR_HAVE_ATOMIC_OPS) 1.28 + 1.29 +#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) 1.30 +/* 1.31 + * PR_AtomicDecrement() is used in NSPR's thread-specific data 1.32 + * destructor. Because thread-specific data destructors may be 1.33 + * invoked after a PR_Cleanup() call, we need an implementation 1.34 + * of the atomic routines that doesn't need NSPR to be initialized. 1.35 + */ 1.36 + 1.37 +/* 1.38 + * We use a set of locks for all the emulated atomic operations. 1.39 + * By hashing on the address of the integer to be locked the 1.40 + * contention between multiple threads should be lessened. 1.41 + * 1.42 + * The number of atomic locks can be set by the environment variable 1.43 + * NSPR_ATOMIC_HASH_LOCKS 1.44 + */ 1.45 + 1.46 +/* 1.47 + * lock counts should be a power of 2 1.48 + */ 1.49 +#define DEFAULT_ATOMIC_LOCKS 16 /* should be in sync with the number of initializers 1.50 + below */ 1.51 +#define MAX_ATOMIC_LOCKS (4 * 1024) 1.52 + 1.53 +static pthread_mutex_t static_atomic_locks[DEFAULT_ATOMIC_LOCKS] = { 1.54 + PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, 1.55 + PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, 1.56 + PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, 1.57 + PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, 1.58 + PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, 1.59 + PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, 1.60 + PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, 1.61 + PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER }; 1.62 + 1.63 +#ifdef DEBUG 1.64 +static PRInt32 static_hash_lock_counts[DEFAULT_ATOMIC_LOCKS]; 1.65 +static PRInt32 *hash_lock_counts = static_hash_lock_counts; 1.66 +#endif 1.67 + 1.68 +static PRUint32 num_atomic_locks = DEFAULT_ATOMIC_LOCKS; 1.69 +static pthread_mutex_t *atomic_locks = static_atomic_locks; 1.70 +static PRUint32 atomic_hash_mask = DEFAULT_ATOMIC_LOCKS - 1; 1.71 + 1.72 +#define _PR_HASH_FOR_LOCK(ptr) \ 1.73 + ((PRUint32) (((PRUptrdiff) (ptr) >> 2) ^ \ 1.74 + ((PRUptrdiff) (ptr) >> 8)) & \ 1.75 + atomic_hash_mask) 1.76 + 1.77 +void _PR_MD_INIT_ATOMIC() 1.78 +{ 1.79 +char *eval; 1.80 +int index; 1.81 + 1.82 + 1.83 + PR_ASSERT(PR_FloorLog2(MAX_ATOMIC_LOCKS) == 1.84 + PR_CeilingLog2(MAX_ATOMIC_LOCKS)); 1.85 + 1.86 + PR_ASSERT(PR_FloorLog2(DEFAULT_ATOMIC_LOCKS) == 1.87 + PR_CeilingLog2(DEFAULT_ATOMIC_LOCKS)); 1.88 + 1.89 + if (((eval = getenv("NSPR_ATOMIC_HASH_LOCKS")) != NULL) && 1.90 + ((num_atomic_locks = atoi(eval)) != DEFAULT_ATOMIC_LOCKS)) { 1.91 + 1.92 + if (num_atomic_locks > MAX_ATOMIC_LOCKS) 1.93 + num_atomic_locks = MAX_ATOMIC_LOCKS; 1.94 + else if (num_atomic_locks < 1) 1.95 + num_atomic_locks = 1; 1.96 + else { 1.97 + num_atomic_locks = PR_FloorLog2(num_atomic_locks); 1.98 + num_atomic_locks = 1L << num_atomic_locks; 1.99 + } 1.100 + atomic_locks = (pthread_mutex_t *) PR_Malloc(sizeof(pthread_mutex_t) * 1.101 + num_atomic_locks); 1.102 + if (atomic_locks) { 1.103 + for (index = 0; index < num_atomic_locks; index++) { 1.104 + if (pthread_mutex_init(&atomic_locks[index], NULL)) { 1.105 + PR_DELETE(atomic_locks); 1.106 + atomic_locks = NULL; 1.107 + break; 1.108 + } 1.109 + } 1.110 + } 1.111 +#ifdef DEBUG 1.112 + if (atomic_locks) { 1.113 + hash_lock_counts = PR_CALLOC(num_atomic_locks * sizeof(PRInt32)); 1.114 + if (hash_lock_counts == NULL) { 1.115 + PR_DELETE(atomic_locks); 1.116 + atomic_locks = NULL; 1.117 + } 1.118 + } 1.119 +#endif 1.120 + if (atomic_locks == NULL) { 1.121 + /* 1.122 + * Use statically allocated locks 1.123 + */ 1.124 + atomic_locks = static_atomic_locks; 1.125 + num_atomic_locks = DEFAULT_ATOMIC_LOCKS; 1.126 + #ifdef DEBUG 1.127 + hash_lock_counts = static_hash_lock_counts; 1.128 + #endif 1.129 + } 1.130 + atomic_hash_mask = num_atomic_locks - 1; 1.131 + } 1.132 + PR_ASSERT(PR_FloorLog2(num_atomic_locks) == 1.133 + PR_CeilingLog2(num_atomic_locks)); 1.134 +} 1.135 + 1.136 +PRInt32 1.137 +_PR_MD_ATOMIC_INCREMENT(PRInt32 *val) 1.138 +{ 1.139 + PRInt32 rv; 1.140 + PRInt32 idx = _PR_HASH_FOR_LOCK(val); 1.141 + 1.142 + pthread_mutex_lock(&atomic_locks[idx]); 1.143 + rv = ++(*val); 1.144 +#ifdef DEBUG 1.145 + hash_lock_counts[idx]++; 1.146 +#endif 1.147 + pthread_mutex_unlock(&atomic_locks[idx]); 1.148 + return rv; 1.149 +} 1.150 + 1.151 +PRInt32 1.152 +_PR_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val) 1.153 +{ 1.154 + PRInt32 rv; 1.155 + PRInt32 idx = _PR_HASH_FOR_LOCK(ptr); 1.156 + 1.157 + pthread_mutex_lock(&atomic_locks[idx]); 1.158 + rv = ((*ptr) += val); 1.159 +#ifdef DEBUG 1.160 + hash_lock_counts[idx]++; 1.161 +#endif 1.162 + pthread_mutex_unlock(&atomic_locks[idx]); 1.163 + return rv; 1.164 +} 1.165 + 1.166 +PRInt32 1.167 +_PR_MD_ATOMIC_DECREMENT(PRInt32 *val) 1.168 +{ 1.169 + PRInt32 rv; 1.170 + PRInt32 idx = _PR_HASH_FOR_LOCK(val); 1.171 + 1.172 + pthread_mutex_lock(&atomic_locks[idx]); 1.173 + rv = --(*val); 1.174 +#ifdef DEBUG 1.175 + hash_lock_counts[idx]++; 1.176 +#endif 1.177 + pthread_mutex_unlock(&atomic_locks[idx]); 1.178 + return rv; 1.179 +} 1.180 + 1.181 +PRInt32 1.182 +_PR_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval) 1.183 +{ 1.184 + PRInt32 rv; 1.185 + PRInt32 idx = _PR_HASH_FOR_LOCK(val); 1.186 + 1.187 + pthread_mutex_lock(&atomic_locks[idx]); 1.188 + rv = *val; 1.189 + *val = newval; 1.190 +#ifdef DEBUG 1.191 + hash_lock_counts[idx]++; 1.192 +#endif 1.193 + pthread_mutex_unlock(&atomic_locks[idx]); 1.194 + return rv; 1.195 +} 1.196 +#else /* _PR_PTHREADS && !_PR_DCETHREADS */ 1.197 +/* 1.198 + * We use a single lock for all the emulated atomic operations. 1.199 + * The lock contention should be acceptable. 1.200 + */ 1.201 +static PRLock *atomic_lock = NULL; 1.202 +void _PR_MD_INIT_ATOMIC(void) 1.203 +{ 1.204 + if (atomic_lock == NULL) { 1.205 + atomic_lock = PR_NewLock(); 1.206 + } 1.207 +} 1.208 + 1.209 +PRInt32 1.210 +_PR_MD_ATOMIC_INCREMENT(PRInt32 *val) 1.211 +{ 1.212 + PRInt32 rv; 1.213 + 1.214 + if (!_pr_initialized) { 1.215 + _PR_ImplicitInitialization(); 1.216 + } 1.217 + PR_Lock(atomic_lock); 1.218 + rv = ++(*val); 1.219 + PR_Unlock(atomic_lock); 1.220 + return rv; 1.221 +} 1.222 + 1.223 +PRInt32 1.224 +_PR_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val) 1.225 +{ 1.226 + PRInt32 rv; 1.227 + 1.228 + if (!_pr_initialized) { 1.229 + _PR_ImplicitInitialization(); 1.230 + } 1.231 + PR_Lock(atomic_lock); 1.232 + rv = ((*ptr) += val); 1.233 + PR_Unlock(atomic_lock); 1.234 + return rv; 1.235 +} 1.236 + 1.237 +PRInt32 1.238 +_PR_MD_ATOMIC_DECREMENT(PRInt32 *val) 1.239 +{ 1.240 + PRInt32 rv; 1.241 + 1.242 + if (!_pr_initialized) { 1.243 + _PR_ImplicitInitialization(); 1.244 + } 1.245 + PR_Lock(atomic_lock); 1.246 + rv = --(*val); 1.247 + PR_Unlock(atomic_lock); 1.248 + return rv; 1.249 +} 1.250 + 1.251 +PRInt32 1.252 +_PR_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval) 1.253 +{ 1.254 + PRInt32 rv; 1.255 + 1.256 + if (!_pr_initialized) { 1.257 + _PR_ImplicitInitialization(); 1.258 + } 1.259 + PR_Lock(atomic_lock); 1.260 + rv = *val; 1.261 + *val = newval; 1.262 + PR_Unlock(atomic_lock); 1.263 + return rv; 1.264 +} 1.265 +#endif /* _PR_PTHREADS && !_PR_DCETHREADS */ 1.266 + 1.267 +#endif /* !_PR_HAVE_ATOMIC_OPS */ 1.268 + 1.269 +void _PR_InitAtomic(void) 1.270 +{ 1.271 + _PR_MD_INIT_ATOMIC(); 1.272 +} 1.273 + 1.274 +PR_IMPLEMENT(PRInt32) 1.275 +PR_AtomicIncrement(PRInt32 *val) 1.276 +{ 1.277 + return _PR_MD_ATOMIC_INCREMENT(val); 1.278 +} 1.279 + 1.280 +PR_IMPLEMENT(PRInt32) 1.281 +PR_AtomicDecrement(PRInt32 *val) 1.282 +{ 1.283 + return _PR_MD_ATOMIC_DECREMENT(val); 1.284 +} 1.285 + 1.286 +PR_IMPLEMENT(PRInt32) 1.287 +PR_AtomicSet(PRInt32 *val, PRInt32 newval) 1.288 +{ 1.289 + return _PR_MD_ATOMIC_SET(val, newval); 1.290 +} 1.291 + 1.292 +PR_IMPLEMENT(PRInt32) 1.293 +PR_AtomicAdd(PRInt32 *ptr, PRInt32 val) 1.294 +{ 1.295 + return _PR_MD_ATOMIC_ADD(ptr, val); 1.296 +} 1.297 +/* 1.298 + * For platforms, which don't support the CAS (compare-and-swap) instruction 1.299 + * (or an equivalent), the stack operations are implemented by use of PRLock 1.300 + */ 1.301 + 1.302 +PR_IMPLEMENT(PRStack *) 1.303 +PR_CreateStack(const char *stack_name) 1.304 +{ 1.305 +PRStack *stack; 1.306 + 1.307 + if (!_pr_initialized) { 1.308 + _PR_ImplicitInitialization(); 1.309 + } 1.310 + 1.311 + if ((stack = PR_NEW(PRStack)) == NULL) { 1.312 + return NULL; 1.313 + } 1.314 + if (stack_name) { 1.315 + stack->prstk_name = (char *) PR_Malloc(strlen(stack_name) + 1); 1.316 + if (stack->prstk_name == NULL) { 1.317 + PR_DELETE(stack); 1.318 + return NULL; 1.319 + } 1.320 + strcpy(stack->prstk_name, stack_name); 1.321 + } else 1.322 + stack->prstk_name = NULL; 1.323 + 1.324 +#ifndef _PR_HAVE_ATOMIC_CAS 1.325 + stack->prstk_lock = PR_NewLock(); 1.326 + if (stack->prstk_lock == NULL) { 1.327 + PR_Free(stack->prstk_name); 1.328 + PR_DELETE(stack); 1.329 + return NULL; 1.330 + } 1.331 +#endif /* !_PR_HAVE_ATOMIC_CAS */ 1.332 + 1.333 + stack->prstk_head.prstk_elem_next = NULL; 1.334 + 1.335 + return stack; 1.336 +} 1.337 + 1.338 +PR_IMPLEMENT(PRStatus) 1.339 +PR_DestroyStack(PRStack *stack) 1.340 +{ 1.341 + if (stack->prstk_head.prstk_elem_next != NULL) { 1.342 + PR_SetError(PR_INVALID_STATE_ERROR, 0); 1.343 + return PR_FAILURE; 1.344 + } 1.345 + 1.346 + if (stack->prstk_name) 1.347 + PR_Free(stack->prstk_name); 1.348 +#ifndef _PR_HAVE_ATOMIC_CAS 1.349 + PR_DestroyLock(stack->prstk_lock); 1.350 +#endif /* !_PR_HAVE_ATOMIC_CAS */ 1.351 + PR_DELETE(stack); 1.352 + 1.353 + return PR_SUCCESS; 1.354 +} 1.355 + 1.356 +#ifndef _PR_HAVE_ATOMIC_CAS 1.357 + 1.358 +PR_IMPLEMENT(void) 1.359 +PR_StackPush(PRStack *stack, PRStackElem *stack_elem) 1.360 +{ 1.361 + PR_Lock(stack->prstk_lock); 1.362 + stack_elem->prstk_elem_next = stack->prstk_head.prstk_elem_next; 1.363 + stack->prstk_head.prstk_elem_next = stack_elem; 1.364 + PR_Unlock(stack->prstk_lock); 1.365 + return; 1.366 +} 1.367 + 1.368 +PR_IMPLEMENT(PRStackElem *) 1.369 +PR_StackPop(PRStack *stack) 1.370 +{ 1.371 +PRStackElem *element; 1.372 + 1.373 + PR_Lock(stack->prstk_lock); 1.374 + element = stack->prstk_head.prstk_elem_next; 1.375 + if (element != NULL) { 1.376 + stack->prstk_head.prstk_elem_next = element->prstk_elem_next; 1.377 + element->prstk_elem_next = NULL; /* debugging aid */ 1.378 + } 1.379 + PR_Unlock(stack->prstk_lock); 1.380 + return element; 1.381 +} 1.382 +#endif /* !_PR_HAVE_ATOMIC_CAS */