Home | History | Annotate | Download | only in common
      1 /*
      2 ******************************************************************************
      3 *
      4 *   Copyright (C) 1997-2012, International Business Machines
      5 *   Corporation and others.  All Rights Reserved.
      6 *
      7 ******************************************************************************
      8 *
      9 * File umutex.cpp
     10 *
     11 * Modification History:
     12 *
     13 *   Date        Name        Description
     14 *   04/02/97    aliu        Creation.
     15 *   04/07/99    srl         updated
     16 *   05/13/99    stephen     Changed to umutex (from cmutex).
     17 *   11/22/99    aliu        Make non-global mutex autoinitialize [j151]
     18 ******************************************************************************
     19 */
     20 
     21 #include "unicode/utypes.h"
     22 #include "uassert.h"
     23 #include "ucln_cmn.h"
     24 
     25 /*
     26  * ICU Mutex wrappers.  Wrap operating system mutexes, giving the rest of ICU a
     27  * platform independent set of mutex operations.  For internal ICU use only.
     28  */
     29 
     30 #if U_PLATFORM_HAS_WIN32_API
     31     /* Prefer native Windows APIs even if POSIX is implemented (i.e., on Cygwin). */
     32 #   undef POSIX
     33 #elif U_PLATFORM_IMPLEMENTS_POSIX
     34 #   define POSIX
     35 #else
     36 #   undef POSIX
     37 #endif
     38 
     39 #if defined(POSIX)
     40 # include <pthread.h> /* must be first, so that we get the multithread versions of things. */
     41 #endif /* POSIX */
     42 
     43 #if U_PLATFORM_HAS_WIN32_API
     44 # define WIN32_LEAN_AND_MEAN
     45 # define VC_EXTRALEAN
     46 # define NOUSER
     47 # define NOSERVICE
     48 # define NOIME
     49 # define NOMCX
     50 # include <windows.h>
     51 #endif
     52 
     53 #include "umutex.h"
     54 #include "cmemory.h"
     55 
     56 #if U_PLATFORM_HAS_WIN32_API
     57 #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
     58             InterlockedCompareExchangePointer(dest, newval, oldval)
     59 
     60 #elif defined(POSIX)
     61 #if (U_HAVE_GCC_ATOMICS == 1)
     62 #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
     63             __sync_val_compare_and_swap(dest, oldval, newval)
     64 #else
     65 #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
     66             mutexed_compare_and_swap(dest, newval, oldval)
     67 #endif
     68 
     69 #else
     70 // Unknown platform.  Note that user can still set mutex functions at run time.
     71 #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
     72             mutexed_compare_and_swap(dest, newval, oldval)
     73 #endif
     74 
     75 static void *mutexed_compare_and_swap(void **dest, void *newval, void *oldval);
     76 
     77 // The ICU global mutex. Used when ICU implementation code passes NULL for the mutex pointer.
     78 static UMutex   globalMutex = U_MUTEX_INITIALIZER;
     79 
     80 // Implementation mutex. Used for compare & swap when no intrinsic is available, and
     81 // for safe initialization of user defined mutexes.
     82 static UMutex   implMutex = U_MUTEX_INITIALIZER;
     83 
     84 // List of all user mutexes that have been initialized.
     85 // Used to allow us to destroy them when cleaning up ICU.
     86 // Normal platform mutexes are not kept track of in this way - they survive until the process is shut down.
     87 // Normal platfrom mutexes don't allocate storage, so not cleaning them up won't trigger memory leak complaints.
     88 //
     89 // Note: putting this list in allocated memory would be awkward to arrange, because memory allocations
     90 //       are used as a flag to indicate that ICU has been initialized, and setting other ICU
     91 //       override functions will no longer work.
     92 //
     93 static const int MUTEX_LIST_LIMIT = 100;
     94 static UMutex *gMutexList[MUTEX_LIST_LIMIT];
     95 static int gMutexListSize = 0;
     96 
     97 
     98 /*
     99  *  User mutex implementation functions.  If non-null, call back to these rather than
    100  *  directly using the system (Posix or Windows) APIs.  See u_setMutexFunctions().
    101  *    (declarations are in uclean.h)
    102  */
    103 static UMtxInitFn    *pMutexInitFn    = NULL;
    104 static UMtxFn        *pMutexDestroyFn = NULL;
    105 static UMtxFn        *pMutexLockFn    = NULL;
    106 static UMtxFn        *pMutexUnlockFn  = NULL;
    107 static const void    *gMutexContext   = NULL;
    108 
    109 
    110 // Clean up (undo) the effects of u_setMutexFunctions().
    111 //
    112 static void usrMutexCleanup() {
    113     if (pMutexDestroyFn != NULL) {
    114         for (int i = 0; i < gMutexListSize; i++) {
    115             UMutex *m = gMutexList[i];
    116             U_ASSERT(m->fInitialized);
    117             (*pMutexDestroyFn)(gMutexContext, &m->fUserMutex);
    118             m->fInitialized = FALSE;
    119         }
    120         (*pMutexDestroyFn)(gMutexContext, &globalMutex.fUserMutex);
    121         (*pMutexDestroyFn)(gMutexContext, &implMutex.fUserMutex);
    122     }
    123     gMutexListSize  = 0;
    124     pMutexInitFn    = NULL;
    125     pMutexDestroyFn = NULL;
    126     pMutexLockFn    = NULL;
    127     pMutexUnlockFn  = NULL;
    128     gMutexContext   = NULL;
    129 }
    130 
    131 
    132 /*
    133  * User mutex lock.
    134  *
    135  * User mutexes need to be initialized before they can be used. We use the impl mutex
    136  * to synchronize the initialization check. This could be sped up on platforms that
    137  * support alternate ways to safely check the initialization flag.
    138  *
    139  */
    140 static void usrMutexLock(UMutex *mutex) {
    141     UErrorCode status = U_ZERO_ERROR;
    142     if (!(mutex == &implMutex || mutex == &globalMutex)) {
    143         umtx_lock(&implMutex);
    144         if (!mutex->fInitialized) {
    145             (*pMutexInitFn)(gMutexContext, &mutex->fUserMutex, &status);
    146             U_ASSERT(U_SUCCESS(status));
    147             mutex->fInitialized = TRUE;
    148             U_ASSERT(gMutexListSize < MUTEX_LIST_LIMIT);
    149             if (gMutexListSize < MUTEX_LIST_LIMIT) {
    150                 gMutexList[gMutexListSize] = mutex;
    151                 ++gMutexListSize;
    152             }
    153         }
    154         umtx_unlock(&implMutex);
    155     }
    156     (*pMutexLockFn)(gMutexContext, &mutex->fUserMutex);
    157 }
    158 
    159 
    160 
    161 #if defined(POSIX)
    162 
    163 //
    164 // POSIX implementation of UMutex.
    165 //
    166 // Each UMutex has a corresponding pthread_mutex_t.
    167 // All are statically initialized and ready for use.
    168 // There is no runtime mutex initialization code needed.
    169 
    170 U_CAPI void  U_EXPORT2
    171 umtx_lock(UMutex *mutex) {
    172     if (mutex == NULL) {
    173         mutex = &globalMutex;
    174     }
    175     if (pMutexLockFn) {
    176         usrMutexLock(mutex);
    177     } else {
    178         #if U_DEBUG
    179             // #if to avoid unused variable warnings in non-debug builds.
    180             int sysErr = pthread_mutex_lock(&mutex->fMutex);
    181             U_ASSERT(sysErr == 0);
    182         #else
    183             pthread_mutex_lock(&mutex->fMutex);
    184         #endif
    185     }
    186 }
    187 
    188 
    189 U_CAPI void  U_EXPORT2
    190 umtx_unlock(UMutex* mutex)
    191 {
    192     if (mutex == NULL) {
    193         mutex = &globalMutex;
    194     }
    195     if (pMutexUnlockFn) {
    196         (*pMutexUnlockFn)(gMutexContext, &mutex->fUserMutex);
    197     } else {
    198         #if U_DEBUG
    199             // #if to avoid unused variable warnings in non-debug builds.
    200             int sysErr = pthread_mutex_unlock(&mutex->fMutex);
    201             U_ASSERT(sysErr == 0);
    202         #else
    203             pthread_mutex_unlock(&mutex->fMutex);
    204         #endif
    205     }
    206 }
    207 
    208 #elif U_PLATFORM_HAS_WIN32_API
    209 //
    210 // Windows implementation of UMutex.
    211 //
    212 // Each UMutex has a corresponding Windows CRITICAL_SECTION.
    213 // CRITICAL_SECTIONS must be initialized before use. This is done
    214 //   with a InitOnceExcuteOnce operation.
    215 //
    216 // InitOnceExecuteOnce was introduced with Windows Vista. For now ICU
    217 // must support Windows XP, so we roll our own. ICU will switch to the
    218 // native Windows InitOnceExecuteOnce when possible.
    219 
    220 typedef UBool (*U_PINIT_ONCE_FN) (
    221   U_INIT_ONCE     *initOnce,
    222   void            *parameter,
    223   void            **context
    224 );
    225 
    226 UBool u_InitOnceExecuteOnce(
    227   U_INIT_ONCE     *initOnce,
    228   U_PINIT_ONCE_FN initFn,
    229   void            *parameter,
    230   void            **context) {
    231       for (;;) {
    232           long previousState = InterlockedCompareExchange(
    233               &initOnce->fState,  //  Destination,
    234               1,                  //  Exchange Value
    235               0);                 //  Compare value
    236           if (previousState == 2) {
    237               // Initialization was already completed.
    238               if (context != NULL) {
    239                   *context = initOnce->fContext;
    240               }
    241               return TRUE;
    242           }
    243           if (previousState == 1) {
    244               // Initialization is in progress in some other thread.
    245               // Loop until it completes.
    246               Sleep(1);
    247               continue;
    248           }
    249 
    250           // Initialization needed. Execute the callback function to do it.
    251           U_ASSERT(previousState == 0);
    252           U_ASSERT(initOnce->fState == 1);
    253           UBool success = (*initFn)(initOnce, parameter, &initOnce->fContext);
    254           U_ASSERT(success); // ICU is not supporting the failure case.
    255 
    256           // Assign the state indicating that initialization has completed.
    257           // Using InterlockedCompareExchange to do it ensures that all
    258           // threads will have a consistent view of memory.
    259           previousState = InterlockedCompareExchange(&initOnce->fState, 2, 1);
    260           U_ASSERT(previousState == 1);
    261           // Next loop iteration will see the initialization and return.
    262       }
    263 };
    264 
    265 static UBool winMutexInit(U_INIT_ONCE *initOnce, void *param, void **context) {
    266     UMutex *mutex = static_cast<UMutex *>(param);
    267     U_ASSERT(sizeof(CRITICAL_SECTION) <= sizeof(mutex->fCS));
    268     InitializeCriticalSection((CRITICAL_SECTION *)mutex->fCS);
    269     return TRUE;
    270 }
    271 
    272 /*
    273  *   umtx_lock
    274  */
    275 U_CAPI void  U_EXPORT2
    276 umtx_lock(UMutex *mutex) {
    277     if (mutex == NULL) {
    278         mutex = &globalMutex;
    279     }
    280     if (pMutexLockFn) {
    281         usrMutexLock(mutex);
    282     } else {
    283         u_InitOnceExecuteOnce(&mutex->fInitOnce, winMutexInit, mutex, NULL);
    284         EnterCriticalSection((CRITICAL_SECTION *)mutex->fCS);
    285     }
    286 }
    287 
    288 U_CAPI void  U_EXPORT2
    289 umtx_unlock(UMutex* mutex)
    290 {
    291     if (mutex == NULL) {
    292         mutex = &globalMutex;
    293     }
    294     if (pMutexUnlockFn) {
    295         (*pMutexUnlockFn)(gMutexContext, &mutex->fUserMutex);
    296     } else {
    297         LeaveCriticalSection((CRITICAL_SECTION *)mutex->fCS);
    298     }
    299 }
    300 
    301 #endif  // Windows Implementation
    302 
    303 
    304 U_CAPI void U_EXPORT2
    305 u_setMutexFunctions(const void *context, UMtxInitFn *i, UMtxFn *d, UMtxFn *l, UMtxFn *u,
    306                     UErrorCode *status) {
    307     if (U_FAILURE(*status)) {
    308         return;
    309     }
    310 
    311     /* Can not set a mutex function to a NULL value  */
    312     if (i==NULL || d==NULL || l==NULL || u==NULL) {
    313         *status = U_ILLEGAL_ARGUMENT_ERROR;
    314         return;
    315     }
    316 
    317     /* If ICU is not in an initial state, disallow this operation. */
    318     if (cmemory_inUse()) {
    319         *status = U_INVALID_STATE_ERROR;
    320         return;
    321     }
    322 
    323     // Clean up any previously set user mutex functions.
    324     // It's possible to call u_setMutexFunctions() more than once without without explicitly cleaning up,
    325     // and the last call should take. Kind of a corner case, but it worked once, there is a test for
    326     // it, so we keep it working. The global and impl mutexes will have been created by the
    327     // previous u_setMutexFunctions(), and now need to be destroyed.
    328 
    329     usrMutexCleanup();
    330 
    331     /* Swap in the mutex function pointers.  */
    332     pMutexInitFn    = i;
    333     pMutexDestroyFn = d;
    334     pMutexLockFn    = l;
    335     pMutexUnlockFn  = u;
    336     gMutexContext   = context;
    337     gMutexListSize  = 0;
    338 
    339     /* Initialize the global and impl mutexes. Safe to do at this point because
    340      * u_setMutexFunctions must be done in a single-threaded envioronment. Not thread safe.
    341      */
    342     (*pMutexInitFn)(gMutexContext, &globalMutex.fUserMutex, status);
    343     globalMutex.fInitialized = TRUE;
    344     (*pMutexInitFn)(gMutexContext, &implMutex.fUserMutex, status);
    345     implMutex.fInitialized = TRUE;
    346 }
    347 
    348 
    349 
    350 /*   synchronized compare and swap function, for use when OS or compiler built-in
    351  *   equivalents aren't available.
    352  */
    353 static void *mutexed_compare_and_swap(void **dest, void *newval, void *oldval) {
    354     umtx_lock(&implMutex);
    355     void *temp = *dest;
    356     if (temp == oldval) {
    357         *dest = newval;
    358     }
    359     umtx_unlock(&implMutex);
    360 
    361     return temp;
    362 }
    363 
    364 
    365 
    366 /*-----------------------------------------------------------------
    367  *
    368  *  Atomic Increment and Decrement
    369  *     umtx_atomic_inc
    370  *     umtx_atomic_dec
    371  *
    372  *----------------------------------------------------------------*/
    373 
    374 /* Pointers to user-supplied inc/dec functions.  Null if no funcs have been set.  */
    375 static UMtxAtomicFn  *pIncFn = NULL;
    376 static UMtxAtomicFn  *pDecFn = NULL;
    377 static const void *gIncDecContext  = NULL;
    378 
    379 #if defined (POSIX) && (U_HAVE_GCC_ATOMICS == 0)
    380 static UMutex   gIncDecMutex = U_MUTEX_INITIALIZER;
    381 #endif
    382 
    383 U_CAPI int32_t U_EXPORT2
    384 umtx_atomic_inc(int32_t *p)  {
    385     int32_t retVal;
    386     if (pIncFn) {
    387         retVal = (*pIncFn)(gIncDecContext, p);
    388     } else {
    389         #if U_PLATFORM_HAS_WIN32_API
    390             retVal = InterlockedIncrement((LONG*)p);
    391         #elif defined(USE_MAC_OS_ATOMIC_INCREMENT)
    392             retVal = OSAtomicIncrement32Barrier(p);
    393         #elif (U_HAVE_GCC_ATOMICS == 1)
    394             retVal = __sync_add_and_fetch(p, 1);
    395         #elif defined (POSIX)
    396             umtx_lock(&gIncDecMutex);
    397             retVal = ++(*p);
    398             umtx_unlock(&gIncDecMutex);
    399         #else
    400             /* Unknown Platform. */
    401             retVal = ++(*p);
    402         #endif
    403     }
    404     return retVal;
    405 }
    406 
    407 U_CAPI int32_t U_EXPORT2
    408 umtx_atomic_dec(int32_t *p) {
    409     int32_t retVal;
    410     if (pDecFn) {
    411         retVal = (*pDecFn)(gIncDecContext, p);
    412     } else {
    413         #if U_PLATFORM_HAS_WIN32_API
    414             retVal = InterlockedDecrement((LONG*)p);
    415         #elif defined(USE_MAC_OS_ATOMIC_INCREMENT)
    416             retVal = OSAtomicDecrement32Barrier(p);
    417         #elif (U_HAVE_GCC_ATOMICS == 1)
    418             retVal = __sync_sub_and_fetch(p, 1);
    419         #elif defined (POSIX)
    420             umtx_lock(&gIncDecMutex);
    421             retVal = --(*p);
    422             umtx_unlock(&gIncDecMutex);
    423         #else
    424             /* Unknown Platform. */
    425             retVal = --(*p);
    426         #endif
    427     }
    428     return retVal;
    429 }
    430 
    431 
    432 
    433 U_CAPI void U_EXPORT2
    434 u_setAtomicIncDecFunctions(const void *context, UMtxAtomicFn *ip, UMtxAtomicFn *dp,
    435                                 UErrorCode *status) {
    436     if (U_FAILURE(*status)) {
    437         return;
    438     }
    439     /* Can not set a mutex function to a NULL value  */
    440     if (ip==NULL || dp==NULL) {
    441         *status = U_ILLEGAL_ARGUMENT_ERROR;
    442         return;
    443     }
    444     /* If ICU is not in an initial state, disallow this operation. */
    445     if (cmemory_inUse()) {
    446         *status = U_INVALID_STATE_ERROR;
    447         return;
    448     }
    449 
    450     pIncFn = ip;
    451     pDecFn = dp;
    452     gIncDecContext = context;
    453 
    454 #if U_DEBUG
    455     {
    456         int32_t   testInt = 0;
    457         U_ASSERT(umtx_atomic_inc(&testInt) == 1);     /* Sanity Check.    Do the functions work at all? */
    458         U_ASSERT(testInt == 1);
    459         U_ASSERT(umtx_atomic_dec(&testInt) == 0);
    460         U_ASSERT(testInt == 0);
    461     }
    462 #endif
    463 }
    464 
    465 
    466 /*
    467  *  Mutex Cleanup Function
    468  *      Reset the mutex function callback pointers.
    469  *      Called from the global ICU u_cleanup() function.
    470  */
    471 U_CFUNC UBool umtx_cleanup(void) {
    472     /* Extra, do-nothing function call to suppress compiler warnings on platforms where
    473      *   mutexed_compare_and_swap is not otherwise used.  */
    474     void *pv = &globalMutex;
    475     mutexed_compare_and_swap(&pv, NULL, NULL);
    476     usrMutexCleanup();
    477 
    478     pIncFn          = NULL;
    479     pDecFn          = NULL;
    480     gIncDecContext  = NULL;
    481 
    482     return TRUE;
    483 }
    484