1 // 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 #ifndef WIN32_LEAN_AND_MEAN 90 # define WIN32_LEAN_AND_MEAN 91 #endif 92 # define VC_EXTRALEAN 93 # define NOUSER 94 # define NOSERVICE 95 # define NOIME 96 # define NOMCX 97 # ifndef NOMINMAX 98 # define NOMINMAX 99 # endif 100 # include <windows.h> 101 102 U_NAMESPACE_BEGIN 103 typedef volatile LONG u_atomic_int32_t; 104 #define ATOMIC_INT32_T_INITIALIZER(val) val 105 106 inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { 107 return InterlockedCompareExchange(&var, 0, 0); 108 } 109 110 inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { 111 InterlockedExchange(&var, val); 112 } 113 114 115 inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) { 116 return InterlockedIncrement(var); 117 } 118 119 inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) { 120 return InterlockedDecrement(var); 121 } 122 U_NAMESPACE_END 123 124 125 #elif U_HAVE_CLANG_ATOMICS 126 /* 127 * Clang __c11 atomic built-ins 128 */ 129 130 U_NAMESPACE_BEGIN 131 typedef _Atomic(int32_t) u_atomic_int32_t; 132 #define ATOMIC_INT32_T_INITIALIZER(val) val 133 134 inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { 135 return __c11_atomic_load(&var, __ATOMIC_ACQUIRE); 136 } 137 138 inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { 139 return __c11_atomic_store(&var, val, __ATOMIC_RELEASE); 140 } 141 142 inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) { 143 return __c11_atomic_fetch_add(var, 1, __ATOMIC_SEQ_CST) + 1; 144 } 145 146 inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) { 147 return __c11_atomic_fetch_sub(var, 1, __ATOMIC_SEQ_CST) - 1; 148 } 149 U_NAMESPACE_END 150 151 152 #elif U_HAVE_GCC_ATOMICS 153 /* 154 * gcc atomic ops. These are available on several other compilers as well. 155 */ 156 157 U_NAMESPACE_BEGIN 158 typedef int32_t u_atomic_int32_t; 159 #define ATOMIC_INT32_T_INITIALIZER(val) val 160 161 inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { 162 int32_t val = var; 163 __sync_synchronize(); 164 return val; 165 } 166 167 inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { 168 __sync_synchronize(); 169 var = val; 170 } 171 172 inline int32_t umtx_atomic_inc(u_atomic_int32_t *p) { 173 return __sync_add_and_fetch(p, 1); 174 } 175 176 inline int32_t umtx_atomic_dec(u_atomic_int32_t *p) { 177 return __sync_sub_and_fetch(p, 1); 178 } 179 U_NAMESPACE_END 180 181 #else 182 183 /* 184 * Unknown Platform. Use out-of-line functions, which in turn use mutexes. 185 * Slow but correct. 186 */ 187 188 #define U_NO_PLATFORM_ATOMICS 189 190 U_NAMESPACE_BEGIN 191 typedef int32_t u_atomic_int32_t; 192 #define ATOMIC_INT32_T_INITIALIZER(val) val 193 194 U_COMMON_API int32_t U_EXPORT2 195 umtx_loadAcquire(u_atomic_int32_t &var); 196 197 U_COMMON_API void U_EXPORT2 198 umtx_storeRelease(u_atomic_int32_t &var, int32_t val); 199 200 U_COMMON_API int32_t U_EXPORT2 201 umtx_atomic_inc(u_atomic_int32_t *p); 202 203 U_COMMON_API int32_t U_EXPORT2 204 umtx_atomic_dec(u_atomic_int32_t *p); 205 206 U_NAMESPACE_END 207 208 #endif /* Low Level Atomic Ops Platfrom Chain */ 209 210 211 212 /************************************************************************************************* 213 * 214 * UInitOnce Definitions. 215 * These are platform neutral. 216 * 217 *************************************************************************************************/ 218 219 U_NAMESPACE_BEGIN 220 221 struct UInitOnce { 222 u_atomic_int32_t fState; 223 UErrorCode fErrCode; 224 void reset() {fState = 0;}; 225 UBool isReset() {return umtx_loadAcquire(fState) == 0;}; 226 // Note: isReset() is used by service registration code. 227 // Thread safety of this usage needs review. 228 }; 229 230 #define U_INITONCE_INITIALIZER {ATOMIC_INT32_T_INITIALIZER(0), U_ZERO_ERROR} 231 232 233 U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &); 234 U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &); 235 236 template<class T> void umtx_initOnce(UInitOnce &uio, T *obj, void (U_CALLCONV T::*fp)()) { 237 if (umtx_loadAcquire(uio.fState) == 2) { 238 return; 239 } 240 if (umtx_initImplPreInit(uio)) { 241 (obj->*fp)(); 242 umtx_initImplPostInit(uio); 243 } 244 } 245 246 247 // umtx_initOnce variant for plain functions, or static class functions. 248 // No context parameter. 249 inline void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)()) { 250 if (umtx_loadAcquire(uio.fState) == 2) { 251 return; 252 } 253 if (umtx_initImplPreInit(uio)) { 254 (*fp)(); 255 umtx_initImplPostInit(uio); 256 } 257 } 258 259 // umtx_initOnce variant for plain functions, or static class functions. 260 // With ErrorCode, No context parameter. 261 inline void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(UErrorCode &), UErrorCode &errCode) { 262 if (U_FAILURE(errCode)) { 263 return; 264 } 265 if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) { 266 // We run the initialization. 267 (*fp)(errCode); 268 uio.fErrCode = errCode; 269 umtx_initImplPostInit(uio); 270 } else { 271 // Someone else already ran the initialization. 272 if (U_FAILURE(uio.fErrCode)) { 273 errCode = uio.fErrCode; 274 } 275 } 276 } 277 278 // umtx_initOnce variant for plain functions, or static class functions, 279 // with a context parameter. 280 template<class T> void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(T), T context) { 281 if (umtx_loadAcquire(uio.fState) == 2) { 282 return; 283 } 284 if (umtx_initImplPreInit(uio)) { 285 (*fp)(context); 286 umtx_initImplPostInit(uio); 287 } 288 } 289 290 // umtx_initOnce variant for plain functions, or static class functions, 291 // with a context parameter and an error code. 292 template<class T> void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(T, UErrorCode &), T context, UErrorCode &errCode) { 293 if (U_FAILURE(errCode)) { 294 return; 295 } 296 if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) { 297 // We run the initialization. 298 (*fp)(context, errCode); 299 uio.fErrCode = errCode; 300 umtx_initImplPostInit(uio); 301 } else { 302 // Someone else already ran the initialization. 303 if (U_FAILURE(uio.fErrCode)) { 304 errCode = uio.fErrCode; 305 } 306 } 307 } 308 309 U_NAMESPACE_END 310 311 312 313 /************************************************************************************************* 314 * 315 * Mutex Definitions. Platform Dependent, #if platform chain follows. 316 * TODO: Add a C++11 version. 317 * Need to convert all mutex using files to C++ first. 318 * 319 *************************************************************************************************/ 320 321 #if defined(U_USER_MUTEX_H) 322 // #inlcude "U_USER_MUTEX_H" 323 #include U_MUTEX_XSTR(U_USER_MUTEX_H) 324 325 #elif U_PLATFORM_USES_ONLY_WIN32_API 326 327 /* For CRITICAL_SECTION */ 328 329 /* 330 * Note: there is an earlier include of windows.h in this file, but it is in 331 * different conditionals. 332 * This one is needed if we are using C++11 for atomic ops, but 333 * win32 APIs for Critical Sections. 334 */ 335 336 #ifndef WIN32_LEAN_AND_MEAN 337 # define WIN32_LEAN_AND_MEAN 338 #endif 339 # define VC_EXTRALEAN 340 # define NOUSER 341 # define NOSERVICE 342 # define NOIME 343 # define NOMCX 344 # ifndef NOMINMAX 345 # define NOMINMAX 346 # endif 347 # include <windows.h> 348 349 350 typedef struct UMutex { 351 icu::UInitOnce fInitOnce; 352 CRITICAL_SECTION fCS; 353 } UMutex; 354 355 /* Initializer for a static UMUTEX. Deliberately contains no value for the 356 * CRITICAL_SECTION. 357 */ 358 #define U_MUTEX_INITIALIZER {U_INITONCE_INITIALIZER} 359 360 struct UConditionVar { 361 HANDLE fEntryGate; 362 HANDLE fExitGate; 363 int32_t fWaitCount; 364 }; 365 366 #define U_CONDITION_INITIALIZER {NULL, NULL, 0} 367 368 369 370 #elif U_PLATFORM_IMPLEMENTS_POSIX 371 372 /* 373 * POSIX platform 374 */ 375 376 #include <pthread.h> 377 378 struct UMutex { 379 pthread_mutex_t fMutex; 380 }; 381 typedef struct UMutex UMutex; 382 #define U_MUTEX_INITIALIZER {PTHREAD_MUTEX_INITIALIZER} 383 384 struct UConditionVar { 385 pthread_cond_t fCondition; 386 }; 387 #define U_CONDITION_INITIALIZER {PTHREAD_COND_INITIALIZER} 388 389 #else 390 391 /* 392 * Unknow platform type. 393 * This is an error condition. ICU requires mutexes. 394 */ 395 396 #error Unknown Platform. 397 398 #endif 399 400 401 402 /************************************************************************************** 403 * 404 * Mutex Implementation function declaratations. 405 * Declarations are platform neutral. 406 * Implementations, in umutex.cpp, are platform specific. 407 * 408 ************************************************************************************/ 409 410 /* Lock a mutex. 411 * @param mutex The given mutex to be locked. Pass NULL to specify 412 * the global ICU mutex. Recursive locks are an error 413 * and may cause a deadlock on some platforms. 414 */ 415 U_INTERNAL void U_EXPORT2 umtx_lock(UMutex* mutex); 416 417 /* Unlock a mutex. 418 * @param mutex The given mutex to be unlocked. Pass NULL to specify 419 * the global ICU mutex. 420 */ 421 U_INTERNAL void U_EXPORT2 umtx_unlock (UMutex* mutex); 422 423 /* 424 * Wait on a condition variable. 425 * The calling thread will unlock the mutex and wait on the condition variable. 426 * The mutex must be locked by the calling thread when invoking this function. 427 * 428 * @param cond the condition variable to wait on. 429 * @param mutex the associated mutex. 430 */ 431 432 U_INTERNAL void U_EXPORT2 umtx_condWait(UConditionVar *cond, UMutex *mutex); 433 434 435 /* 436 * Broadcast wakeup of all threads waiting on a Condition. 437 * The associated mutex must be locked by the calling thread when calling 438 * this function; this is a temporary ICU restriction. 439 * 440 * @param cond the condition variable. 441 */ 442 U_INTERNAL void U_EXPORT2 umtx_condBroadcast(UConditionVar *cond); 443 444 /* 445 * Signal a condition variable, waking up one waiting thread. 446 * CAUTION: Do not use. Place holder only. Not implemented for Windows. 447 */ 448 U_INTERNAL void U_EXPORT2 umtx_condSignal(UConditionVar *cond); 449 450 #endif /* UMUTEX_H */ 451 /*eof*/ 452