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