Home | History | Annotate | Download | only in common
      1 // Copyright (C) 2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 /*
      4 **********************************************************************
      5 *   Copyright (C) 1997-2015, International Business Machines
      6 *   Corporation and others.  All Rights Reserved.
      7 **********************************************************************
      8 *
      9 * File UMUTEX.H
     10 *
     11 * Modification History:
     12 *
     13 *   Date        Name        Description
     14 *   04/02/97  aliu        Creation.
     15 *   04/07/99  srl         rewrite - C interface, multiple mutices
     16 *   05/13/99  stephen     Changed to umutex (from cmutex)
     17 ******************************************************************************
     18 */
     19 
     20 #ifndef UMUTEX_H
     21 #define UMUTEX_H
     22 
     23 #include "unicode/utypes.h"
     24 #include "unicode/uclean.h"
     25 #include "putilimp.h"
     26 
     27 
     28 
     29 // Forward Declarations. UMutex is not in the ICU namespace (yet) because
     30 //                       there are some remaining references from plain C.
     31 struct UMutex;
     32 struct UConditionVar;
     33 
     34 U_NAMESPACE_BEGIN
     35 struct UInitOnce;
     36 U_NAMESPACE_END
     37 
     38 // Stringify macros, to allow #include of user supplied atomic & mutex files.
     39 #define U_MUTEX_STR(s) #s
     40 #define U_MUTEX_XSTR(s) U_MUTEX_STR(s)
     41 
     42 /****************************************************************************
     43  *
     44  *   Low Level Atomic Operations.
     45  *      Compiler dependent. Not operating system dependent.
     46  *
     47  ****************************************************************************/
     48 #if defined (U_USER_ATOMICS_H)
     49 #include U_MUTEX_XSTR(U_USER_ATOMICS_H)
     50 
     51 #elif U_HAVE_STD_ATOMICS
     52 
     53 //  C++11 atomics are available.
     54 
     55 #include <atomic>
     56 
     57 U_NAMESPACE_BEGIN
     58 
     59 typedef std::atomic<int32_t> u_atomic_int32_t;
     60 #define ATOMIC_INT32_T_INITIALIZER(val) ATOMIC_VAR_INIT(val)
     61 
     62 inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
     63     return var.load(std::memory_order_acquire);
     64 }
     65 
     66 inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
     67     var.store(val, std::memory_order_release);
     68 }
     69 
     70 inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
     71     return var->fetch_add(1) + 1;
     72 }
     73 
     74 inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
     75     return var->fetch_sub(1) - 1;
     76 }
     77 U_NAMESPACE_END
     78 
     79 #elif U_PLATFORM_HAS_WIN32_API
     80 
     81 // MSVC compiler. Reads and writes of volatile variables have
     82 //                acquire and release memory semantics, respectively.
     83 //                This is a Microsoft extension, not standard C++ behavior.
     84 //
     85 //   Update:      can't use this because of MinGW, built with gcc.
     86 //                Original plan was to use gcc atomics for MinGW, but they
     87 //                aren't supported, so we fold MinGW into this path.
     88 
     89 # define WIN32_LEAN_AND_MEAN
     90 # define VC_EXTRALEAN
     91 # define NOUSER
     92 # define NOSERVICE
     93 # define NOIME
     94 # define NOMCX
     95 # ifndef NOMINMAX
     96 # define NOMINMAX
     97 # endif
     98 # include <windows.h>
     99 
    100 U_NAMESPACE_BEGIN
    101 typedef volatile LONG u_atomic_int32_t;
    102 #define ATOMIC_INT32_T_INITIALIZER(val) val
    103 
    104 inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
    105     return InterlockedCompareExchange(&var, 0, 0);
    106 }
    107 
    108 inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
    109     InterlockedExchange(&var, val);
    110 }
    111 
    112 
    113 inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
    114     return InterlockedIncrement(var);
    115 }
    116 
    117 inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
    118     return InterlockedDecrement(var);
    119 }
    120 U_NAMESPACE_END
    121 
    122 
    123 #elif U_HAVE_CLANG_ATOMICS
    124 /*
    125  *  Clang __c11 atomic built-ins
    126  */
    127 
    128 U_NAMESPACE_BEGIN
    129 typedef _Atomic(int32_t) u_atomic_int32_t;
    130 #define ATOMIC_INT32_T_INITIALIZER(val) val
    131 
    132 inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
    133      return __c11_atomic_load(&var, __ATOMIC_ACQUIRE);
    134 }
    135 
    136 inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
    137    return __c11_atomic_store(&var, val, __ATOMIC_RELEASE);
    138 }
    139 
    140 inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
    141     return __c11_atomic_fetch_add(var, 1, __ATOMIC_SEQ_CST) + 1;
    142 }
    143 
    144 inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
    145     return __c11_atomic_fetch_sub(var, 1, __ATOMIC_SEQ_CST) - 1;
    146 }
    147 U_NAMESPACE_END
    148 
    149 
    150 #elif U_HAVE_GCC_ATOMICS
    151 /*
    152  * gcc atomic ops. These are available on several other compilers as well.
    153  */
    154 
    155 U_NAMESPACE_BEGIN
    156 typedef int32_t u_atomic_int32_t;
    157 #define ATOMIC_INT32_T_INITIALIZER(val) val
    158 
    159 inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
    160     int32_t val = var;
    161     __sync_synchronize();
    162     return val;
    163 }
    164 
    165 inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
    166     __sync_synchronize();
    167     var = val;
    168 }
    169 
    170 inline int32_t umtx_atomic_inc(u_atomic_int32_t *p)  {
    171    return __sync_add_and_fetch(p, 1);
    172 }
    173 
    174 inline int32_t umtx_atomic_dec(u_atomic_int32_t *p)  {
    175    return __sync_sub_and_fetch(p, 1);
    176 }
    177 U_NAMESPACE_END
    178 
    179 #else
    180 
    181 /*
    182  * Unknown Platform. Use out-of-line functions, which in turn use mutexes.
    183  *                   Slow but correct.
    184  */
    185 
    186 #define U_NO_PLATFORM_ATOMICS
    187 
    188 U_NAMESPACE_BEGIN
    189 typedef int32_t u_atomic_int32_t;
    190 #define ATOMIC_INT32_T_INITIALIZER(val) val
    191 
    192 U_COMMON_API int32_t U_EXPORT2
    193 umtx_loadAcquire(u_atomic_int32_t &var);
    194 
    195 U_COMMON_API void U_EXPORT2
    196 umtx_storeRelease(u_atomic_int32_t &var, int32_t val);
    197 
    198 U_COMMON_API int32_t U_EXPORT2
    199 umtx_atomic_inc(u_atomic_int32_t *p);
    200 
    201 U_COMMON_API int32_t U_EXPORT2
    202 umtx_atomic_dec(u_atomic_int32_t *p);
    203 
    204 U_NAMESPACE_END
    205 
    206 #endif  /* Low Level Atomic Ops Platfrom Chain */
    207 
    208 
    209 
    210 /*************************************************************************************************
    211  *
    212  *  UInitOnce Definitions.
    213  *     These are platform neutral.
    214  *
    215  *************************************************************************************************/
    216 
    217 U_NAMESPACE_BEGIN
    218 
    219 struct UInitOnce {
    220     u_atomic_int32_t   fState;
    221     UErrorCode       fErrCode;
    222     void reset() {fState = 0;};
    223     UBool isReset() {return umtx_loadAcquire(fState) == 0;};
    224 // Note: isReset() is used by service registration code.
    225 //                 Thread safety of this usage needs review.
    226 };
    227 
    228 #define U_INITONCE_INITIALIZER {ATOMIC_INT32_T_INITIALIZER(0), U_ZERO_ERROR}
    229 
    230 
    231 U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &);
    232 U_COMMON_API void  U_EXPORT2 umtx_initImplPostInit(UInitOnce &);
    233 
    234 template<class T> void umtx_initOnce(UInitOnce &uio, T *obj, void (U_CALLCONV T::*fp)()) {
    235     if (umtx_loadAcquire(uio.fState) == 2) {
    236         return;
    237     }
    238     if (umtx_initImplPreInit(uio)) {
    239         (obj->*fp)();
    240         umtx_initImplPostInit(uio);
    241     }
    242 }
    243 
    244 
    245 // umtx_initOnce variant for plain functions, or static class functions.
    246 //               No context parameter.
    247 inline void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)()) {
    248     if (umtx_loadAcquire(uio.fState) == 2) {
    249         return;
    250     }
    251     if (umtx_initImplPreInit(uio)) {
    252         (*fp)();
    253         umtx_initImplPostInit(uio);
    254     }
    255 }
    256 
    257 // umtx_initOnce variant for plain functions, or static class functions.
    258 //               With ErrorCode, No context parameter.
    259 inline void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(UErrorCode &), UErrorCode &errCode) {
    260     if (U_FAILURE(errCode)) {
    261         return;
    262     }
    263     if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) {
    264         // We run the initialization.
    265         (*fp)(errCode);
    266         uio.fErrCode = errCode;
    267         umtx_initImplPostInit(uio);
    268     } else {
    269         // Someone else already ran the initialization.
    270         if (U_FAILURE(uio.fErrCode)) {
    271             errCode = uio.fErrCode;
    272         }
    273     }
    274 }
    275 
    276 // umtx_initOnce variant for plain functions, or static class functions,
    277 //               with a context parameter.
    278 template<class T> void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(T), T context) {
    279     if (umtx_loadAcquire(uio.fState) == 2) {
    280         return;
    281     }
    282     if (umtx_initImplPreInit(uio)) {
    283         (*fp)(context);
    284         umtx_initImplPostInit(uio);
    285     }
    286 }
    287 
    288 // umtx_initOnce variant for plain functions, or static class functions,
    289 //               with a context parameter and an error code.
    290 template<class T> void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(T, UErrorCode &), T context, UErrorCode &errCode) {
    291     if (U_FAILURE(errCode)) {
    292         return;
    293     }
    294     if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) {
    295         // We run the initialization.
    296         (*fp)(context, errCode);
    297         uio.fErrCode = errCode;
    298         umtx_initImplPostInit(uio);
    299     } else {
    300         // Someone else already ran the initialization.
    301         if (U_FAILURE(uio.fErrCode)) {
    302             errCode = uio.fErrCode;
    303         }
    304     }
    305 }
    306 
    307 U_NAMESPACE_END
    308 
    309 
    310 
    311 /*************************************************************************************************
    312  *
    313  *  Mutex Definitions. Platform Dependent, #if platform chain follows.
    314  *         TODO:  Add a C++11 version.
    315  *                Need to convert all mutex using files to C++ first.
    316  *
    317  *************************************************************************************************/
    318 
    319 #if defined(U_USER_MUTEX_H)
    320 // #inlcude "U_USER_MUTEX_H"
    321 #include U_MUTEX_XSTR(U_USER_MUTEX_H)
    322 
    323 #elif U_PLATFORM_USES_ONLY_WIN32_API
    324 
    325 /* For CRITICAL_SECTION */
    326 
    327 /*
    328  *   Note: there is an earlier include of windows.h in this file, but it is in
    329  *         different conditionals.
    330  *         This one is needed if we are using C++11 for atomic ops, but
    331  *         win32 APIs for Critical Sections.
    332  */
    333 
    334 # define WIN32_LEAN_AND_MEAN
    335 # define VC_EXTRALEAN
    336 # define NOUSER
    337 # define NOSERVICE
    338 # define NOIME
    339 # define NOMCX
    340 # ifndef NOMINMAX
    341 # define NOMINMAX
    342 # endif
    343 # include <windows.h>
    344 
    345 
    346 typedef struct UMutex {
    347     icu::UInitOnce    fInitOnce;
    348     CRITICAL_SECTION  fCS;
    349 } UMutex;
    350 
    351 /* Initializer for a static UMUTEX. Deliberately contains no value for the
    352  *  CRITICAL_SECTION.
    353  */
    354 #define U_MUTEX_INITIALIZER {U_INITONCE_INITIALIZER}
    355 
    356 struct UConditionVar {
    357     HANDLE           fEntryGate;
    358     HANDLE           fExitGate;
    359     int32_t          fWaitCount;
    360 };
    361 
    362 #define U_CONDITION_INITIALIZER {NULL, NULL, 0}
    363 
    364 
    365 
    366 #elif U_PLATFORM_IMPLEMENTS_POSIX
    367 
    368 /*
    369  *  POSIX platform
    370  */
    371 
    372 #include <pthread.h>
    373 
    374 struct UMutex {
    375     pthread_mutex_t  fMutex;
    376 };
    377 typedef struct UMutex UMutex;
    378 #define U_MUTEX_INITIALIZER  {PTHREAD_MUTEX_INITIALIZER}
    379 
    380 struct UConditionVar {
    381     pthread_cond_t   fCondition;
    382 };
    383 #define U_CONDITION_INITIALIZER {PTHREAD_COND_INITIALIZER}
    384 
    385 #else
    386 
    387 /*
    388  *  Unknow platform type.
    389  *      This is an error condition. ICU requires mutexes.
    390  */
    391 
    392 #error Unknown Platform.
    393 
    394 #endif
    395 
    396 
    397 
    398 /**************************************************************************************
    399  *
    400  *  Mutex Implementation function declaratations.
    401  *     Declarations are platform neutral.
    402  *     Implementations, in umutex.cpp, are platform specific.
    403  *
    404  ************************************************************************************/
    405 
    406 /* Lock a mutex.
    407  * @param mutex The given mutex to be locked.  Pass NULL to specify
    408  *              the global ICU mutex.  Recursive locks are an error
    409  *              and may cause a deadlock on some platforms.
    410  */
    411 U_INTERNAL void U_EXPORT2 umtx_lock(UMutex* mutex);
    412 
    413 /* Unlock a mutex.
    414  * @param mutex The given mutex to be unlocked.  Pass NULL to specify
    415  *              the global ICU mutex.
    416  */
    417 U_INTERNAL void U_EXPORT2 umtx_unlock (UMutex* mutex);
    418 
    419 /*
    420  * Wait on a condition variable.
    421  * The calling thread will unlock the mutex and wait on the condition variable.
    422  * The mutex must be locked by the calling thread when invoking this function.
    423  *
    424  * @param cond the condition variable to wait on.
    425  * @param mutex the associated mutex.
    426  */
    427 
    428 U_INTERNAL void U_EXPORT2 umtx_condWait(UConditionVar *cond, UMutex *mutex);
    429 
    430 
    431 /*
    432  * Broadcast wakeup of all threads waiting on a Condition.
    433  * The associated mutex must be locked by the calling thread when calling
    434  * this function; this is a temporary ICU restriction.
    435  *
    436  * @param cond the condition variable.
    437  */
    438 U_INTERNAL void U_EXPORT2 umtx_condBroadcast(UConditionVar *cond);
    439 
    440 /*
    441  * Signal a condition variable, waking up one waiting thread.
    442  * CAUTION: Do not use. Place holder only. Not implemented for Windows.
    443  */
    444 U_INTERNAL void U_EXPORT2 umtx_condSignal(UConditionVar *cond);
    445 
    446 #endif /* UMUTEX_H */
    447 /*eof*/
    448