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. 31struct UMutex; 32struct UConditionVar; 33 34U_NAMESPACE_BEGIN 35struct UInitOnce; 36U_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 57U_NAMESPACE_BEGIN 58 59typedef std::atomic<int32_t> u_atomic_int32_t; 60#define ATOMIC_INT32_T_INITIALIZER(val) ATOMIC_VAR_INIT(val) 61 62inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { 63 return var.load(std::memory_order_acquire); 64} 65 66inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { 67 var.store(val, std::memory_order_release); 68} 69 70inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) { 71 return var->fetch_add(1) + 1; 72} 73 74inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) { 75 return var->fetch_sub(1) - 1; 76} 77U_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 100U_NAMESPACE_BEGIN 101typedef volatile LONG u_atomic_int32_t; 102#define ATOMIC_INT32_T_INITIALIZER(val) val 103 104inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { 105 return InterlockedCompareExchange(&var, 0, 0); 106} 107 108inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { 109 InterlockedExchange(&var, val); 110} 111 112 113inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) { 114 return InterlockedIncrement(var); 115} 116 117inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) { 118 return InterlockedDecrement(var); 119} 120U_NAMESPACE_END 121 122 123#elif U_HAVE_CLANG_ATOMICS 124/* 125 * Clang __c11 atomic built-ins 126 */ 127 128U_NAMESPACE_BEGIN 129typedef _Atomic(int32_t) u_atomic_int32_t; 130#define ATOMIC_INT32_T_INITIALIZER(val) val 131 132inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { 133 return __c11_atomic_load(&var, __ATOMIC_ACQUIRE); 134} 135 136inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { 137 return __c11_atomic_store(&var, val, __ATOMIC_RELEASE); 138} 139 140inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) { 141 return __c11_atomic_fetch_add(var, 1, __ATOMIC_SEQ_CST) + 1; 142} 143 144inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) { 145 return __c11_atomic_fetch_sub(var, 1, __ATOMIC_SEQ_CST) - 1; 146} 147U_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 155U_NAMESPACE_BEGIN 156typedef int32_t u_atomic_int32_t; 157#define ATOMIC_INT32_T_INITIALIZER(val) val 158 159inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { 160 int32_t val = var; 161 __sync_synchronize(); 162 return val; 163} 164 165inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { 166 __sync_synchronize(); 167 var = val; 168} 169 170inline int32_t umtx_atomic_inc(u_atomic_int32_t *p) { 171 return __sync_add_and_fetch(p, 1); 172} 173 174inline int32_t umtx_atomic_dec(u_atomic_int32_t *p) { 175 return __sync_sub_and_fetch(p, 1); 176} 177U_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 188U_NAMESPACE_BEGIN 189typedef int32_t u_atomic_int32_t; 190#define ATOMIC_INT32_T_INITIALIZER(val) val 191 192U_COMMON_API int32_t U_EXPORT2 193umtx_loadAcquire(u_atomic_int32_t &var); 194 195U_COMMON_API void U_EXPORT2 196umtx_storeRelease(u_atomic_int32_t &var, int32_t val); 197 198U_COMMON_API int32_t U_EXPORT2 199umtx_atomic_inc(u_atomic_int32_t *p); 200 201U_COMMON_API int32_t U_EXPORT2 202umtx_atomic_dec(u_atomic_int32_t *p); 203 204U_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 217U_NAMESPACE_BEGIN 218 219struct 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 231U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &); 232U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &); 233 234template<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. 247inline 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. 259inline 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. 278template<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. 290template<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 307U_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 346typedef 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 356struct 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 374struct UMutex { 375 pthread_mutex_t fMutex; 376}; 377typedef struct UMutex UMutex; 378#define U_MUTEX_INITIALIZER {PTHREAD_MUTEX_INITIALIZER} 379 380struct 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 */ 411U_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 */ 417U_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 428U_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 */ 438U_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 */ 444U_INTERNAL void U_EXPORT2 umtx_condSignal(UConditionVar *cond); 445 446#endif /* UMUTEX_H */ 447/*eof*/ 448