Back to index

lightning-sunbird  0.9+nobinonly
pratom.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039 **     PR Atomic operations
00040 */
00041 
00042 
00043 #include "pratom.h"
00044 #include "primpl.h"
00045 
00046 #include <string.h>
00047 
00048 /*
00049  * The following is a fallback implementation that emulates
00050  * atomic operations for platforms without atomic operations.
00051  * If a platform has atomic operations, it should define the
00052  * macro _PR_HAVE_ATOMIC_OPS, and the following will not be
00053  * compiled in.
00054  */
00055 
00056 #if !defined(_PR_HAVE_ATOMIC_OPS)
00057 
00058 #if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
00059 /*
00060  * PR_AtomicDecrement() is used in NSPR's thread-specific data
00061  * destructor.  Because thread-specific data destructors may be
00062  * invoked after a PR_Cleanup() call, we need an implementation
00063  * of the atomic routines that doesn't need NSPR to be initialized.
00064  */
00065 
00066 /*
00067  * We use a set of locks for all the emulated atomic operations.
00068  * By hashing on the address of the integer to be locked the
00069  * contention between multiple threads should be lessened.
00070  *
00071  * The number of atomic locks can be set by the environment variable
00072  * NSPR_ATOMIC_HASH_LOCKS
00073  */
00074 
00075 /*
00076  * lock counts should be a power of 2
00077  */
00078 #define DEFAULT_ATOMIC_LOCKS       16     /* should be in sync with the number of initializers
00079                                                                       below */
00080 #define MAX_ATOMIC_LOCKS           (4 * 1024)
00081 
00082 static pthread_mutex_t static_atomic_locks[DEFAULT_ATOMIC_LOCKS] = {
00083         PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
00084         PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
00085         PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
00086         PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
00087         PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
00088         PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
00089         PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
00090         PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER };
00091 
00092 #ifdef DEBUG
00093 static PRInt32 static_hash_lock_counts[DEFAULT_ATOMIC_LOCKS];
00094 static PRInt32 *hash_lock_counts = static_hash_lock_counts;
00095 #endif
00096 
00097 static PRUint32      num_atomic_locks = DEFAULT_ATOMIC_LOCKS;
00098 static pthread_mutex_t *atomic_locks = static_atomic_locks;
00099 static PRUint32 atomic_hash_mask = DEFAULT_ATOMIC_LOCKS - 1;
00100 
00101 #define _PR_HASH_FOR_LOCK(ptr)                                               \
00102                      ((PRUint32) (((PRUptrdiff) (ptr) >> 2)    ^      \
00103                                           ((PRUptrdiff) (ptr) >> 8)) &       \
00104                                           atomic_hash_mask)
00105 
00106 void _PR_MD_INIT_ATOMIC()
00107 {
00108 char *eval;
00109 int index;
00110 
00111 
00112        PR_ASSERT(PR_FloorLog2(MAX_ATOMIC_LOCKS) ==
00113                                           PR_CeilingLog2(MAX_ATOMIC_LOCKS));
00114 
00115        PR_ASSERT(PR_FloorLog2(DEFAULT_ATOMIC_LOCKS) ==
00116                                                  PR_CeilingLog2(DEFAULT_ATOMIC_LOCKS));
00117 
00118        if (((eval = getenv("NSPR_ATOMIC_HASH_LOCKS")) != NULL)  &&
00119               ((num_atomic_locks = atoi(eval)) != DEFAULT_ATOMIC_LOCKS)) {
00120 
00121               if (num_atomic_locks > MAX_ATOMIC_LOCKS)
00122                      num_atomic_locks = MAX_ATOMIC_LOCKS;
00123               else if (num_atomic_locks < 1) 
00124                      num_atomic_locks = 1;
00125               else {
00126                      num_atomic_locks = PR_FloorLog2(num_atomic_locks);
00127                      num_atomic_locks = 1L << num_atomic_locks;
00128               }
00129               atomic_locks = (pthread_mutex_t *) PR_Malloc(sizeof(pthread_mutex_t) *
00130                                           num_atomic_locks);
00131               if (atomic_locks) {
00132                      for (index = 0; index < num_atomic_locks; index++) {
00133                             if (pthread_mutex_init(&atomic_locks[index], NULL)) {
00134                                           PR_DELETE(atomic_locks);
00135                                           atomic_locks = NULL;
00136                                           break; 
00137                             }
00138                      }
00139               }
00140 #ifdef DEBUG
00141               if (atomic_locks) {
00142                      hash_lock_counts = PR_CALLOC(num_atomic_locks * sizeof(PRInt32));
00143                      if (hash_lock_counts == NULL) {
00144                             PR_DELETE(atomic_locks);
00145                             atomic_locks = NULL;
00146                      }
00147               }
00148 #endif
00149               if (atomic_locks == NULL) {
00150                      /*
00151                       *     Use statically allocated locks
00152                       */
00153                      atomic_locks = static_atomic_locks;
00154                      num_atomic_locks = DEFAULT_ATOMIC_LOCKS;
00155        #ifdef DEBUG
00156                      hash_lock_counts = static_hash_lock_counts;
00157        #endif
00158               }
00159               atomic_hash_mask = num_atomic_locks - 1;
00160        }
00161        PR_ASSERT(PR_FloorLog2(num_atomic_locks) ==
00162                                                         PR_CeilingLog2(num_atomic_locks));
00163 }
00164 
00165 PRInt32
00166 _PR_MD_ATOMIC_INCREMENT(PRInt32 *val)
00167 {
00168     PRInt32 rv;
00169     PRInt32 idx = _PR_HASH_FOR_LOCK(val);
00170 
00171     pthread_mutex_lock(&atomic_locks[idx]);
00172     rv = ++(*val);
00173 #ifdef DEBUG
00174     hash_lock_counts[idx]++;
00175 #endif
00176     pthread_mutex_unlock(&atomic_locks[idx]);
00177     return rv;
00178 }
00179 
00180 PRInt32
00181 _PR_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val)
00182 {
00183     PRInt32 rv;
00184     PRInt32 idx = _PR_HASH_FOR_LOCK(ptr);
00185 
00186     pthread_mutex_lock(&atomic_locks[idx]);
00187     rv = ((*ptr) += val);
00188 #ifdef DEBUG
00189     hash_lock_counts[idx]++;
00190 #endif
00191     pthread_mutex_unlock(&atomic_locks[idx]);
00192     return rv;
00193 }
00194 
00195 PRInt32
00196 _PR_MD_ATOMIC_DECREMENT(PRInt32 *val)
00197 {
00198     PRInt32 rv;
00199     PRInt32 idx = _PR_HASH_FOR_LOCK(val);
00200 
00201     pthread_mutex_lock(&atomic_locks[idx]);
00202     rv = --(*val);
00203 #ifdef DEBUG
00204     hash_lock_counts[idx]++;
00205 #endif
00206     pthread_mutex_unlock(&atomic_locks[idx]);
00207     return rv;
00208 }
00209 
00210 PRInt32
00211 _PR_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
00212 {
00213     PRInt32 rv;
00214     PRInt32 idx = _PR_HASH_FOR_LOCK(val);
00215 
00216     pthread_mutex_lock(&atomic_locks[idx]);
00217     rv = *val;
00218     *val = newval;
00219 #ifdef DEBUG
00220     hash_lock_counts[idx]++;
00221 #endif
00222     pthread_mutex_unlock(&atomic_locks[idx]);
00223     return rv;
00224 }
00225 #else  /* _PR_PTHREADS && !_PR_DCETHREADS */
00226 /*
00227  * We use a single lock for all the emulated atomic operations.
00228  * The lock contention should be acceptable.
00229  */
00230 static PRLock *atomic_lock = NULL;
00231 void _PR_MD_INIT_ATOMIC(void)
00232 {
00233     if (atomic_lock == NULL) {
00234         atomic_lock = PR_NewLock();
00235     }
00236 }
00237 
00238 PRInt32
00239 _PR_MD_ATOMIC_INCREMENT(PRInt32 *val)
00240 {
00241     PRInt32 rv;
00242 
00243     if (!_pr_initialized) {
00244         _PR_ImplicitInitialization();
00245     }
00246     PR_Lock(atomic_lock);
00247     rv = ++(*val);
00248     PR_Unlock(atomic_lock);
00249     return rv;
00250 }
00251 
00252 PRInt32
00253 _PR_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val)
00254 {
00255     PRInt32 rv;
00256 
00257     if (!_pr_initialized) {
00258         _PR_ImplicitInitialization();
00259     }
00260     PR_Lock(atomic_lock);
00261     rv = ((*ptr) += val);
00262     PR_Unlock(atomic_lock);
00263     return rv;
00264 }
00265 
00266 PRInt32
00267 _PR_MD_ATOMIC_DECREMENT(PRInt32 *val)
00268 {
00269     PRInt32 rv;
00270 
00271     if (!_pr_initialized) {
00272         _PR_ImplicitInitialization();
00273     }
00274     PR_Lock(atomic_lock);
00275     rv = --(*val);
00276     PR_Unlock(atomic_lock);
00277     return rv;
00278 }
00279 
00280 PRInt32
00281 _PR_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
00282 {
00283     PRInt32 rv;
00284 
00285     if (!_pr_initialized) {
00286         _PR_ImplicitInitialization();
00287     }
00288     PR_Lock(atomic_lock);
00289     rv = *val;
00290     *val = newval;
00291     PR_Unlock(atomic_lock);
00292     return rv;
00293 }
00294 #endif  /* _PR_PTHREADS && !_PR_DCETHREADS */
00295 
00296 #endif  /* !_PR_HAVE_ATOMIC_OPS */
00297 
00298 void _PR_InitAtomic(void)
00299 {
00300     _PR_MD_INIT_ATOMIC();
00301 }
00302 
00303 PR_IMPLEMENT(PRInt32)
00304 PR_AtomicIncrement(PRInt32 *val)
00305 {
00306     return _PR_MD_ATOMIC_INCREMENT(val);
00307 }
00308 
00309 PR_IMPLEMENT(PRInt32)
00310 PR_AtomicDecrement(PRInt32 *val)
00311 {
00312     return _PR_MD_ATOMIC_DECREMENT(val);
00313 }
00314 
00315 PR_IMPLEMENT(PRInt32)
00316 PR_AtomicSet(PRInt32 *val, PRInt32 newval)
00317 {
00318     return _PR_MD_ATOMIC_SET(val, newval);
00319 }
00320 
00321 PR_IMPLEMENT(PRInt32)
00322 PR_AtomicAdd(PRInt32 *ptr, PRInt32 val)
00323 {
00324     return _PR_MD_ATOMIC_ADD(ptr, val);
00325 }
00326 /*
00327  * For platforms, which don't support the CAS (compare-and-swap) instruction
00328  * (or an equivalent), the stack operations are implemented by use of PRLock
00329  */
00330 
00331 PR_IMPLEMENT(PRStack *)
00332 PR_CreateStack(const char *stack_name)
00333 {
00334 PRStack *stack;
00335 
00336     if (!_pr_initialized) {
00337         _PR_ImplicitInitialization();
00338     }
00339 
00340     if ((stack = PR_NEW(PRStack)) == NULL) {
00341               return NULL;
00342        }
00343        if (stack_name) {
00344               stack->prstk_name = (char *) PR_Malloc(strlen(stack_name) + 1);
00345               if (stack->prstk_name == NULL) {
00346                      PR_DELETE(stack);
00347                      return NULL;
00348               }
00349               strcpy(stack->prstk_name, stack_name);
00350        } else
00351               stack->prstk_name = NULL;
00352 
00353 #ifndef _PR_HAVE_ATOMIC_CAS
00354     stack->prstk_lock = PR_NewLock();
00355        if (stack->prstk_lock == NULL) {
00356               PR_Free(stack->prstk_name);
00357               PR_DELETE(stack);
00358               return NULL;
00359        }
00360 #endif /* !_PR_HAVE_ATOMIC_CAS */
00361 
00362        stack->prstk_head.prstk_elem_next = NULL;
00363        
00364     return stack;
00365 }
00366 
00367 PR_IMPLEMENT(PRStatus)
00368 PR_DestroyStack(PRStack *stack)
00369 {
00370        if (stack->prstk_head.prstk_elem_next != NULL) {
00371               PR_SetError(PR_INVALID_STATE_ERROR, 0);
00372               return PR_FAILURE;
00373        }
00374 
00375        if (stack->prstk_name)
00376               PR_Free(stack->prstk_name);
00377 #ifndef _PR_HAVE_ATOMIC_CAS
00378        PR_DestroyLock(stack->prstk_lock);
00379 #endif /* !_PR_HAVE_ATOMIC_CAS */
00380        PR_DELETE(stack);
00381 
00382        return PR_SUCCESS;
00383 }
00384 
00385 #ifndef _PR_HAVE_ATOMIC_CAS
00386 
00387 PR_IMPLEMENT(void)
00388 PR_StackPush(PRStack *stack, PRStackElem *stack_elem)
00389 {
00390     PR_Lock(stack->prstk_lock);
00391        stack_elem->prstk_elem_next = stack->prstk_head.prstk_elem_next;
00392        stack->prstk_head.prstk_elem_next = stack_elem;
00393     PR_Unlock(stack->prstk_lock);
00394     return;
00395 }
00396 
00397 PR_IMPLEMENT(PRStackElem *)
00398 PR_StackPop(PRStack *stack)
00399 {
00400 PRStackElem *element;
00401 
00402     PR_Lock(stack->prstk_lock);
00403        element = stack->prstk_head.prstk_elem_next;
00404        if (element != NULL) {
00405               stack->prstk_head.prstk_elem_next = element->prstk_elem_next;
00406               element->prstk_elem_next = NULL;   /* debugging aid */
00407        }
00408     PR_Unlock(stack->prstk_lock);
00409     return element;
00410 }
00411 #endif /* !_PR_HAVE_ATOMIC_CAS */