Home | History | Annotate | Download | only in common
      1 /*
      2 ******************************************************************************
      3 *
      4 *   Copyright (C) 1997-2013, 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 "umutex.h"
     22 
     23 #include "unicode/utypes.h"
     24 #include "uassert.h"
     25 #include "cmemory.h"
     26 #include "ucln_cmn.h"
     27 
     28 
     29 // The ICU global mutex. Used when ICU implementation code passes NULL for the mutex pointer.
     30 static UMutex   globalMutex = U_MUTEX_INITIALIZER;
     31 
     32 /*
     33  * ICU Mutex wrappers.  Wrap operating system mutexes, giving the rest of ICU a
     34  * platform independent set of mutex operations.  For internal ICU use only.
     35  */
     36 
     37 #if defined(U_USER_MUTEX_CPP)
     38 // Build time user mutex hook: #include "U_USER_MUTEX_CPP"
     39 #include U_MUTEX_XSTR(U_USER_MUTEX_CPP)
     40 
     41 #elif U_PLATFORM_HAS_WIN32_API
     42 
     43 //-------------------------------------------------------------------------------------------
     44 //
     45 //    Windows Specific Definitions
     46 //
     47 //        Note: Cygwin (and possibly others) have both WIN32 and POSIX.
     48 //              Prefer Win32 in these cases.  (Win32 comes ahead in the #if chain)
     49 //
     50 //-------------------------------------------------------------------------------------------
     51 
     52 #if defined U_NO_PLATFORM_ATOMICS
     53 #error ICU on Win32 requires support for low level atomic operations.
     54 // Visual Studio, gcc, clang are OK. Shouldn't get here.
     55 #endif
     56 
     57 
     58 // This function is called when a test of a UInitOnce::fState reveals that
     59 //   initialization has not completed, that we either need to call the
     60 //   function on this thread, or wait for some other thread to complete.
     61 //
     62 // The actual call to the init function is made inline by template code
     63 //   that knows the C++ types involved. This function returns TRUE if
     64 //   the caller needs to call the Init function.
     65 //
     66 
     67 U_NAMESPACE_BEGIN
     68 
     69 U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &uio) {
     70     for (;;) {
     71         int32_t previousState = InterlockedCompareExchange(
     72 #if (U_PLATFORM == U_PF_MINGW) || (U_PLATFORM == U_PF_CYGWIN)
     73            (LONG volatile *) // this is the type given in the API doc for this function.
     74 #endif
     75             &uio.fState,  //  Destination
     76             1,            //  Exchange Value
     77             0);           //  Compare value
     78 
     79         if (previousState == 0) {
     80             return true;   // Caller will next call the init function.
     81                            // Current state == 1.
     82         } else if (previousState == 2) {
     83             // Another thread already completed the initialization.
     84             //   We can simply return FALSE, indicating no
     85             //   further action is needed by the caller.
     86             return FALSE;
     87         } else {
     88             // Another thread is currently running the initialization.
     89             // Wait until it completes.
     90             do {
     91                 Sleep(1);
     92                 previousState = umtx_loadAcquire(uio.fState);
     93             } while (previousState == 1);
     94         }
     95     }
     96 }
     97 
     98 // This function is called by the thread that ran an initialization function,
     99 // just after completing the function.
    100 //
    101 //   success: True:  the inialization succeeded. No further calls to the init
    102 //                   function will be made.
    103 //            False: the initializtion failed. The next call to umtx_initOnce()
    104 //                   will retry the initialization.
    105 
    106 U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &uio) {
    107     umtx_storeRelease(uio.fState, 2);
    108 }
    109 
    110 U_NAMESPACE_END
    111 
    112 static void winMutexInit(CRITICAL_SECTION *cs) {
    113     InitializeCriticalSection(cs);
    114     return;
    115 }
    116 
    117 U_CAPI void  U_EXPORT2
    118 umtx_lock(UMutex *mutex) {
    119     if (mutex == NULL) {
    120         mutex = &globalMutex;
    121     }
    122     CRITICAL_SECTION *cs = &mutex->fCS;
    123     umtx_initOnce(mutex->fInitOnce, winMutexInit, cs);
    124     EnterCriticalSection(cs);
    125 }
    126 
    127 U_CAPI void  U_EXPORT2
    128 umtx_unlock(UMutex* mutex)
    129 {
    130     if (mutex == NULL) {
    131         mutex = &globalMutex;
    132     }
    133     LeaveCriticalSection(&mutex->fCS);
    134 }
    135 
    136 #elif U_PLATFORM_IMPLEMENTS_POSIX
    137 
    138 //-------------------------------------------------------------------------------------------
    139 //
    140 //  POSIX specific definitions
    141 //
    142 //-------------------------------------------------------------------------------------------
    143 
    144 # include <pthread.h>
    145 
    146 // Each UMutex consists of a pthread_mutex_t.
    147 // All are statically initialized and ready for use.
    148 // There is no runtime mutex initialization code needed.
    149 
    150 U_CAPI void  U_EXPORT2
    151 umtx_lock(UMutex *mutex) {
    152     if (mutex == NULL) {
    153         mutex = &globalMutex;
    154     }
    155     int sysErr = pthread_mutex_lock(&mutex->fMutex);
    156     (void)sysErr;   // Suppress unused variable warnings.
    157     U_ASSERT(sysErr == 0);
    158 }
    159 
    160 
    161 U_CAPI void  U_EXPORT2
    162 umtx_unlock(UMutex* mutex)
    163 {
    164     if (mutex == NULL) {
    165         mutex = &globalMutex;
    166     }
    167     int sysErr = pthread_mutex_unlock(&mutex->fMutex);
    168     (void)sysErr;   // Suppress unused variable warnings.
    169     U_ASSERT(sysErr == 0);
    170 }
    171 
    172 U_NAMESPACE_BEGIN
    173 
    174 static pthread_mutex_t initMutex = PTHREAD_MUTEX_INITIALIZER;
    175 static pthread_cond_t initCondition = PTHREAD_COND_INITIALIZER;
    176 
    177 
    178 // This function is called when a test of a UInitOnce::fState reveals that
    179 //   initialization has not completed, that we either need to call the
    180 //   function on this thread, or wait for some other thread to complete.
    181 //
    182 // The actual call to the init function is made inline by template code
    183 //   that knows the C++ types involved. This function returns TRUE if
    184 //   the caller needs to call the Init function.
    185 //
    186 U_COMMON_API UBool U_EXPORT2
    187 umtx_initImplPreInit(UInitOnce &uio) {
    188     pthread_mutex_lock(&initMutex);
    189     int32_t state = uio.fState;
    190     if (state == 0) {
    191         umtx_storeRelease(uio.fState, 1);
    192         pthread_mutex_unlock(&initMutex);
    193         return TRUE;   // Caller will next call the init function.
    194     } else {
    195         while (uio.fState == 1) {
    196             // Another thread is currently running the initialization.
    197             // Wait until it completes.
    198             pthread_cond_wait(&initCondition, &initMutex);
    199         }
    200         pthread_mutex_unlock(&initMutex);
    201         U_ASSERT(uio.fState == 2);
    202         return FALSE;
    203     }
    204 }
    205 
    206 
    207 
    208 // This function is called by the thread that ran an initialization function,
    209 // just after completing the function.
    210 //   Some threads may be waiting on the condition, requiring the broadcast wakeup.
    211 //   Some threads may be racing to test the fState variable outside of the mutex,
    212 //   requiring the use of store/release when changing its value.
    213 
    214 U_COMMON_API void U_EXPORT2
    215 umtx_initImplPostInit(UInitOnce &uio) {
    216     pthread_mutex_lock(&initMutex);
    217     umtx_storeRelease(uio.fState, 2);
    218     pthread_cond_broadcast(&initCondition);
    219     pthread_mutex_unlock(&initMutex);
    220 }
    221 
    222 U_NAMESPACE_END
    223 
    224 // End of POSIX specific umutex implementation.
    225 
    226 #else  // Platform #define chain.
    227 
    228 #error Unknown Platform
    229 
    230 #endif  // Platform #define chain.
    231 
    232 
    233 //-------------------------------------------------------------------------------
    234 //
    235 //   Atomic Operations, out-of-line versions.
    236 //                      These are conditional, only defined if better versions
    237 //                      were not available for the platform.
    238 //
    239 //                      These versions are platform neutral.
    240 //
    241 //--------------------------------------------------------------------------------
    242 
    243 #if defined U_NO_PLATFORM_ATOMICS
    244 static UMutex   gIncDecMutex = U_MUTEX_INITIALIZER;
    245 
    246 U_NAMESPACE_BEGIN
    247 
    248 U_COMMON_API int32_t U_EXPORT2
    249 umtx_atomic_inc(u_atomic_int32_t *p)  {
    250     int32_t retVal;
    251     umtx_lock(&gIncDecMutex);
    252     retVal = ++(*p);
    253     umtx_unlock(&gIncDecMutex);
    254     return retVal;
    255 }
    256 
    257 
    258 U_COMMON_API int32_t U_EXPORT2
    259 umtx_atomic_dec(u_atomic_int32_t *p) {
    260     int32_t retVal;
    261     umtx_lock(&gIncDecMutex);
    262     retVal = --(*p);
    263     umtx_unlock(&gIncDecMutex);
    264     return retVal;
    265 }
    266 
    267 U_COMMON_API int32_t U_EXPORT2
    268 umtx_loadAcquire(u_atomic_int32_t &var) {
    269     int32_t val = var;
    270     umtx_lock(&gIncDecMutex);
    271     umtx_unlock(&gIncDecMutex);
    272     return val;
    273 }
    274 
    275 U_COMMON_API void U_EXPORT2
    276 umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
    277     umtx_lock(&gIncDecMutex);
    278     umtx_unlock(&gIncDecMutex);
    279     var = val;
    280 }
    281 
    282 U_NAMESPACE_END
    283 #endif
    284 
    285 //--------------------------------------------------------------------------
    286 //
    287 //  Deprecated functions for setting user mutexes.
    288 //
    289 //--------------------------------------------------------------------------
    290 
    291 U_DEPRECATED void U_EXPORT2
    292 u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *,
    293                     UMtxFn *,  UMtxFn *, UErrorCode *status) {
    294     if (U_SUCCESS(*status)) {
    295         *status = U_UNSUPPORTED_ERROR;
    296     }
    297     return;
    298 }
    299 
    300 
    301 
    302 U_DEPRECATED void U_EXPORT2
    303 u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *,
    304                            UErrorCode *status) {
    305     if (U_SUCCESS(*status)) {
    306         *status = U_UNSUPPORTED_ERROR;
    307     }
    308     return;
    309 }
    310