Home | History | Annotate | Download | only in common
      1 //  2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 /*
      4 ******************************************************************************
      5 *
      6 *   Copyright (C) 1997-2016, International Business Machines
      7 *   Corporation and others.  All Rights Reserved.
      8 *
      9 ******************************************************************************
     10 *
     11 * File umutex.cpp
     12 *
     13 * Modification History:
     14 *
     15 *   Date        Name        Description
     16 *   04/02/97    aliu        Creation.
     17 *   04/07/99    srl         updated
     18 *   05/13/99    stephen     Changed to umutex (from cmutex).
     19 *   11/22/99    aliu        Make non-global mutex autoinitialize [j151]
     20 ******************************************************************************
     21 */
     22 
     23 #include "umutex.h"
     24 
     25 #include "unicode/utypes.h"
     26 #include "uassert.h"
     27 #include "cmemory.h"
     28 
     29 
     30 // The ICU global mutex. Used when ICU implementation code passes NULL for the mutex pointer.
     31 static UMutex   globalMutex = U_MUTEX_INITIALIZER;
     32 
     33 /*
     34  * ICU Mutex wrappers.  Wrap operating system mutexes, giving the rest of ICU a
     35  * platform independent set of mutex operations.  For internal ICU use only.
     36  */
     37 
     38 #if defined(U_USER_MUTEX_CPP)
     39 // Build time user mutex hook: #include "U_USER_MUTEX_CPP"
     40 #include U_MUTEX_XSTR(U_USER_MUTEX_CPP)
     41 
     42 #elif U_PLATFORM_USES_ONLY_WIN32_API
     43 
     44 #if defined U_NO_PLATFORM_ATOMICS
     45 #error ICU on Win32 requires support for low level atomic operations.
     46 // Visual Studio, gcc, clang are OK. Shouldn't get here.
     47 #endif
     48 
     49 
     50 // This function is called when a test of a UInitOnce::fState reveals that
     51 //   initialization has not completed, that we either need to call the
     52 //   function on this thread, or wait for some other thread to complete.
     53 //
     54 // The actual call to the init function is made inline by template code
     55 //   that knows the C++ types involved. This function returns TRUE if
     56 //   the caller needs to call the Init function.
     57 //
     58 
     59 U_NAMESPACE_BEGIN
     60 
     61 U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &uio) {
     62     for (;;) {
     63         int32_t previousState = InterlockedCompareExchange(
     64             (LONG volatile *) // this is the type given in the API doc for this function.
     65                 &uio.fState,  //  Destination
     66             1,            //  Exchange Value
     67             0);           //  Compare value
     68 
     69         if (previousState == 0) {
     70             return true;   // Caller will next call the init function.
     71                            // Current state == 1.
     72         } else if (previousState == 2) {
     73             // Another thread already completed the initialization.
     74             //   We can simply return FALSE, indicating no
     75             //   further action is needed by the caller.
     76             return FALSE;
     77         } else {
     78             // Another thread is currently running the initialization.
     79             // Wait until it completes.
     80             do {
     81                 Sleep(1);
     82                 previousState = umtx_loadAcquire(uio.fState);
     83             } while (previousState == 1);
     84         }
     85     }
     86 }
     87 
     88 // This function is called by the thread that ran an initialization function,
     89 // just after completing the function.
     90 
     91 U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &uio) {
     92     umtx_storeRelease(uio.fState, 2);
     93 }
     94 
     95 U_NAMESPACE_END
     96 
     97 static void winMutexInit(CRITICAL_SECTION *cs) {
     98     InitializeCriticalSection(cs);
     99     return;
    100 }
    101 
    102 U_CAPI void  U_EXPORT2
    103 umtx_lock(UMutex *mutex) {
    104     if (mutex == NULL) {
    105         mutex = &globalMutex;
    106     }
    107     CRITICAL_SECTION *cs = &mutex->fCS;
    108     umtx_initOnce(mutex->fInitOnce, winMutexInit, cs);
    109     EnterCriticalSection(cs);
    110 }
    111 
    112 U_CAPI void  U_EXPORT2
    113 umtx_unlock(UMutex* mutex)
    114 {
    115     if (mutex == NULL) {
    116         mutex = &globalMutex;
    117     }
    118     LeaveCriticalSection(&mutex->fCS);
    119 }
    120 
    121 
    122 U_CAPI void U_EXPORT2
    123 umtx_condBroadcast(UConditionVar *condition) {
    124     // We require that the associated mutex be held by the caller,
    125     //  so access to fWaitCount is protected and safe. No other thread can
    126     //  call condWait() while we are here.
    127     if (condition->fWaitCount == 0) {
    128         return;
    129     }
    130     ResetEvent(condition->fExitGate);
    131     SetEvent(condition->fEntryGate);
    132 }
    133 
    134 U_CAPI void U_EXPORT2
    135 umtx_condSignal(UConditionVar * /* condition */) {
    136     // Function not implemented. There is no immediate requirement from ICU to have it.
    137     // Once ICU drops support for Windows XP and Server 2003, ICU Condition Variables will be
    138     // changed to be thin wrappers on native Windows CONDITION_VARIABLEs, and this function
    139     // becomes trivial to provide.
    140     U_ASSERT(FALSE);
    141 }
    142 
    143 U_CAPI void U_EXPORT2
    144 umtx_condWait(UConditionVar *condition, UMutex *mutex) {
    145     if (condition->fEntryGate == NULL) {
    146         // Note: because the associated mutex must be locked when calling
    147         //       wait, we know that there can not be multiple threads
    148         //       running here with the same condition variable.
    149         //       Meaning that lazy initialization is safe.
    150         U_ASSERT(condition->fExitGate == NULL);
    151         condition->fEntryGate = CreateEvent(NULL,   // Security Attributes
    152                                             TRUE,   // Manual Reset
    153                                             FALSE,  // Initially reset
    154                                             NULL);  // Name.
    155         U_ASSERT(condition->fEntryGate != NULL);
    156         condition->fExitGate = CreateEvent(NULL, TRUE, TRUE, NULL);
    157         U_ASSERT(condition->fExitGate != NULL);
    158     }
    159 
    160     condition->fWaitCount++;
    161     umtx_unlock(mutex);
    162     WaitForSingleObject(condition->fEntryGate, INFINITE);
    163     umtx_lock(mutex);
    164     condition->fWaitCount--;
    165     if (condition->fWaitCount == 0) {
    166         // All threads that were waiting at the entry gate have woken up
    167         // and moved through. Shut the entry gate and open the exit gate.
    168         ResetEvent(condition->fEntryGate);
    169         SetEvent(condition->fExitGate);
    170     } else {
    171         umtx_unlock(mutex);
    172         WaitForSingleObject(condition->fExitGate, INFINITE);
    173         umtx_lock(mutex);
    174     }
    175 }
    176 
    177 
    178 #elif U_PLATFORM_IMPLEMENTS_POSIX
    179 
    180 //-------------------------------------------------------------------------------------------
    181 //
    182 //  POSIX specific definitions
    183 //
    184 //-------------------------------------------------------------------------------------------
    185 
    186 # include <pthread.h>
    187 
    188 // Each UMutex consists of a pthread_mutex_t.
    189 // All are statically initialized and ready for use.
    190 // There is no runtime mutex initialization code needed.
    191 
    192 U_CAPI void  U_EXPORT2
    193 umtx_lock(UMutex *mutex) {
    194     if (mutex == NULL) {
    195         mutex = &globalMutex;
    196     }
    197     int sysErr = pthread_mutex_lock(&mutex->fMutex);
    198     (void)sysErr;   // Suppress unused variable warnings.
    199     U_ASSERT(sysErr == 0);
    200 }
    201 
    202 
    203 U_CAPI void  U_EXPORT2
    204 umtx_unlock(UMutex* mutex)
    205 {
    206     if (mutex == NULL) {
    207         mutex = &globalMutex;
    208     }
    209     int sysErr = pthread_mutex_unlock(&mutex->fMutex);
    210     (void)sysErr;   // Suppress unused variable warnings.
    211     U_ASSERT(sysErr == 0);
    212 }
    213 
    214 
    215 U_CAPI void U_EXPORT2
    216 umtx_condWait(UConditionVar *cond, UMutex *mutex) {
    217     if (mutex == NULL) {
    218         mutex = &globalMutex;
    219     }
    220     int sysErr = pthread_cond_wait(&cond->fCondition, &mutex->fMutex);
    221     (void)sysErr;
    222     U_ASSERT(sysErr == 0);
    223 }
    224 
    225 U_CAPI void U_EXPORT2
    226 umtx_condBroadcast(UConditionVar *cond) {
    227     int sysErr = pthread_cond_broadcast(&cond->fCondition);
    228     (void)sysErr;
    229     U_ASSERT(sysErr == 0);
    230 }
    231 
    232 U_CAPI void U_EXPORT2
    233 umtx_condSignal(UConditionVar *cond) {
    234     int sysErr = pthread_cond_signal(&cond->fCondition);
    235     (void)sysErr;
    236     U_ASSERT(sysErr == 0);
    237 }
    238 
    239 
    240 
    241 U_NAMESPACE_BEGIN
    242 
    243 static pthread_mutex_t initMutex = PTHREAD_MUTEX_INITIALIZER;
    244 static pthread_cond_t initCondition = PTHREAD_COND_INITIALIZER;
    245 
    246 
    247 // This function is called when a test of a UInitOnce::fState reveals that
    248 //   initialization has not completed, that we either need to call the
    249 //   function on this thread, or wait for some other thread to complete.
    250 //
    251 // The actual call to the init function is made inline by template code
    252 //   that knows the C++ types involved. This function returns TRUE if
    253 //   the caller needs to call the Init function.
    254 //
    255 U_COMMON_API UBool U_EXPORT2
    256 umtx_initImplPreInit(UInitOnce &uio) {
    257     pthread_mutex_lock(&initMutex);
    258     int32_t state = uio.fState;
    259     if (state == 0) {
    260         umtx_storeRelease(uio.fState, 1);
    261         pthread_mutex_unlock(&initMutex);
    262         return TRUE;   // Caller will next call the init function.
    263     } else {
    264         while (uio.fState == 1) {
    265             // Another thread is currently running the initialization.
    266             // Wait until it completes.
    267             pthread_cond_wait(&initCondition, &initMutex);
    268         }
    269         pthread_mutex_unlock(&initMutex);
    270         U_ASSERT(uio.fState == 2);
    271         return FALSE;
    272     }
    273 }
    274 
    275 
    276 
    277 // This function is called by the thread that ran an initialization function,
    278 // just after completing the function.
    279 //   Some threads may be waiting on the condition, requiring the broadcast wakeup.
    280 //   Some threads may be racing to test the fState variable outside of the mutex,
    281 //   requiring the use of store/release when changing its value.
    282 
    283 U_COMMON_API void U_EXPORT2
    284 umtx_initImplPostInit(UInitOnce &uio) {
    285     pthread_mutex_lock(&initMutex);
    286     umtx_storeRelease(uio.fState, 2);
    287     pthread_cond_broadcast(&initCondition);
    288     pthread_mutex_unlock(&initMutex);
    289 }
    290 
    291 U_NAMESPACE_END
    292 
    293 // End of POSIX specific umutex implementation.
    294 
    295 #else  // Platform #define chain.
    296 
    297 #error Unknown Platform
    298 
    299 #endif  // Platform #define chain.
    300 
    301 
    302 //-------------------------------------------------------------------------------
    303 //
    304 //   Atomic Operations, out-of-line versions.
    305 //                      These are conditional, only defined if better versions
    306 //                      were not available for the platform.
    307 //
    308 //                      These versions are platform neutral.
    309 //
    310 //--------------------------------------------------------------------------------
    311 
    312 #if defined U_NO_PLATFORM_ATOMICS
    313 static UMutex   gIncDecMutex = U_MUTEX_INITIALIZER;
    314 
    315 U_NAMESPACE_BEGIN
    316 
    317 U_COMMON_API int32_t U_EXPORT2
    318 umtx_atomic_inc(u_atomic_int32_t *p)  {
    319     int32_t retVal;
    320     umtx_lock(&gIncDecMutex);
    321     retVal = ++(*p);
    322     umtx_unlock(&gIncDecMutex);
    323     return retVal;
    324 }
    325 
    326 
    327 U_COMMON_API int32_t U_EXPORT2
    328 umtx_atomic_dec(u_atomic_int32_t *p) {
    329     int32_t retVal;
    330     umtx_lock(&gIncDecMutex);
    331     retVal = --(*p);
    332     umtx_unlock(&gIncDecMutex);
    333     return retVal;
    334 }
    335 
    336 U_COMMON_API int32_t U_EXPORT2
    337 umtx_loadAcquire(u_atomic_int32_t &var) {
    338     umtx_lock(&gIncDecMutex);
    339     int32_t val = var;
    340     umtx_unlock(&gIncDecMutex);
    341     return val;
    342 }
    343 
    344 U_COMMON_API void U_EXPORT2
    345 umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
    346     umtx_lock(&gIncDecMutex);
    347     var = val;
    348     umtx_unlock(&gIncDecMutex);
    349 }
    350 
    351 U_NAMESPACE_END
    352 #endif
    353 
    354 //--------------------------------------------------------------------------
    355 //
    356 //  Deprecated functions for setting user mutexes.
    357 //
    358 //--------------------------------------------------------------------------
    359 
    360 U_DEPRECATED void U_EXPORT2
    361 u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *,
    362                     UMtxFn *,  UMtxFn *, UErrorCode *status) {
    363     if (U_SUCCESS(*status)) {
    364         *status = U_UNSUPPORTED_ERROR;
    365     }
    366     return;
    367 }
    368 
    369 
    370 
    371 U_DEPRECATED void U_EXPORT2
    372 u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *,
    373                            UErrorCode *status) {
    374     if (U_SUCCESS(*status)) {
    375         *status = U_UNSUPPORTED_ERROR;
    376     }
    377     return;
    378 }
    379