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