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 #ifndef __wasi__ 24 #include <atomic> 25 #include <condition_variable> 26 #include <mutex> 27 #endif 28 29 #include <type_traits> 30 31 #include "unicode/utypes.h" 32 #include "unicode/uclean.h" 33 #include "unicode/uobject.h" 34 35 #include "putilimp.h" 36 37 #if defined(U_USER_ATOMICS_H) || defined(U_USER_MUTEX_H) 38 // Support for including an alternate implementation of atomic & mutex operations has been withdrawn. 39 // See issue ICU-20185. 40 #error U_USER_ATOMICS and U_USER_MUTEX_H are not supported 41 #endif 42 43 #ifndef __wasi__ 44 45 // Export an explicit template instantiation of std::atomic<int32_t>. 46 // When building DLLs for Windows this is required as it is used as a data member of the exported SharedObject class. 47 // See digitlst.h, pluralaffix.h, datefmt.h, and others for similar examples. 48 // 49 // Similar story for std::atomic<std::mutex *>, and the exported UMutex class. 50 #if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN && !defined(U_IN_DOXYGEN) 51 #if defined(__clang__) || defined(_MSC_VER) 52 #if defined(__clang__) 53 // Suppress the warning that the explicit instantiation after explicit specialization has no effect. 54 #pragma clang diagnostic push 55 #pragma clang diagnostic ignored "-Winstantiation-after-specialization" 56 #endif 57 template struct U_COMMON_API std::atomic<int32_t>; 58 template struct U_COMMON_API std::atomic<std::mutex *>; 59 #if defined(__clang__) 60 #pragma clang diagnostic pop 61 #endif 62 #elif defined(__GNUC__) 63 // For GCC this class is already exported/visible, so no need for U_COMMON_API. 64 template struct std::atomic<int32_t>; 65 template struct std::atomic<std::mutex *>; 66 #endif 67 #endif 68 69 #endif 70 71 U_NAMESPACE_BEGIN 72 73 /**************************************************************************** 74 * 75 * Low Level Atomic Operations, ICU wrappers for. 76 * 77 ****************************************************************************/ 78 79 #ifndef __wasi__ 80 81 typedef std::atomic<int32_t> u_atomic_int32_t; 82 #define ATOMIC_INT32_T_INITIALIZER(val) ATOMIC_VAR_INIT(val) 83 84 inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { 85 return var.load(std::memory_order_acquire); 86 } 87 88 inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { 89 var.store(val, std::memory_order_release); 90 } 91 92 inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) { 93 return var->fetch_add(1) + 1; 94 } 95 96 inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) { 97 return var->fetch_sub(1) - 1; 98 } 99 100 #else 101 102 typedef int32_t u_atomic_int32_t; 103 #define ATOMIC_INT32_T_INITIALIZER(val) val 104 105 inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { 106 return var; 107 } 108 109 inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { 110 var = val; 111 } 112 113 inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) { 114 return ++(*var); 115 } 116 117 inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) { 118 return --(*var); 119 } 120 121 #endif 122 123 /************************************************************************************************* 124 * 125 * UInitOnce Definitions. 126 * 127 *************************************************************************************************/ 128 129 struct UInitOnce { 130 u_atomic_int32_t fState; 131 UErrorCode fErrCode; 132 void reset() {fState = 0;} 133 UBool isReset() {return umtx_loadAcquire(fState) == 0;} 134 // Note: isReset() is used by service registration code. 135 // Thread safety of this usage needs review. 136 }; 137 138 #define U_INITONCE_INITIALIZER {ATOMIC_INT32_T_INITIALIZER(0), U_ZERO_ERROR} 139 140 141 U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &); 142 U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &); 143 144 template<class T> void umtx_initOnce(UInitOnce &uio, T *obj, void (U_CALLCONV T::*fp)()) { 145 if (umtx_loadAcquire(uio.fState) == 2) { 146 return; 147 } 148 if (umtx_initImplPreInit(uio)) { 149 (obj->*fp)(); 150 umtx_initImplPostInit(uio); 151 } 152 } 153 154 155 // umtx_initOnce variant for plain functions, or static class functions. 156 // No context parameter. 157 inline void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)()) { 158 if (umtx_loadAcquire(uio.fState) == 2) { 159 return; 160 } 161 if (umtx_initImplPreInit(uio)) { 162 (*fp)(); 163 umtx_initImplPostInit(uio); 164 } 165 } 166 167 // umtx_initOnce variant for plain functions, or static class functions. 168 // With ErrorCode, No context parameter. 169 inline void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(UErrorCode &), UErrorCode &errCode) { 170 if (U_FAILURE(errCode)) { 171 return; 172 } 173 if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) { 174 // We run the initialization. 175 (*fp)(errCode); 176 uio.fErrCode = errCode; 177 umtx_initImplPostInit(uio); 178 } else { 179 // Someone else already ran the initialization. 180 if (U_FAILURE(uio.fErrCode)) { 181 errCode = uio.fErrCode; 182 } 183 } 184 } 185 186 // umtx_initOnce variant for plain functions, or static class functions, 187 // with a context parameter. 188 template<class T> void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(T), T context) { 189 if (umtx_loadAcquire(uio.fState) == 2) { 190 return; 191 } 192 if (umtx_initImplPreInit(uio)) { 193 (*fp)(context); 194 umtx_initImplPostInit(uio); 195 } 196 } 197 198 // umtx_initOnce variant for plain functions, or static class functions, 199 // with a context parameter and an error code. 200 template<class T> void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(T, UErrorCode &), T context, UErrorCode &errCode) { 201 if (U_FAILURE(errCode)) { 202 return; 203 } 204 if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) { 205 // We run the initialization. 206 (*fp)(context, errCode); 207 uio.fErrCode = errCode; 208 umtx_initImplPostInit(uio); 209 } else { 210 // Someone else already ran the initialization. 211 if (U_FAILURE(uio.fErrCode)) { 212 errCode = uio.fErrCode; 213 } 214 } 215 } 216 217 // UMutex should be constexpr-constructible, so that no initialization code 218 // is run during startup. 219 // This works on all C++ libraries except MS VS before VS2019. 220 #if (defined(_CPPLIB_VER) && !defined(_MSVC_STL_VERSION)) || \ 221 (defined(_MSVC_STL_VERSION) && _MSVC_STL_VERSION < 142) 222 // (VS std lib older than VS2017) || (VS std lib version < VS2019) 223 # define UMUTEX_CONSTEXPR 224 #else 225 # define UMUTEX_CONSTEXPR constexpr 226 #endif 227 228 /** 229 * UMutex - ICU Mutex class. 230 * 231 * This is the preferred Mutex class for use within ICU implementation code. 232 * It is a thin wrapper over C++ std::mutex, with these additions: 233 * - Static instances are safe, not triggering static construction or destruction, 234 * and the associated order of construction or destruction issues. 235 * - Plumbed into u_cleanup() for destructing the underlying std::mutex, 236 * which frees any OS level resources they may be holding. 237 * 238 * Limitations: 239 * - Static or global instances only. Cannot be heap allocated. Cannot appear as a 240 * member of another class. 241 * - No condition variables or other advanced features. If needed, you will need to use 242 * std::mutex and std::condition_variable directly. For an example, see unifiedcache.cpp 243 * 244 * Typical Usage: 245 * static UMutex myMutex; 246 * 247 * { 248 * Mutex lock(myMutex); 249 * ... // Do stuff that is protected by myMutex; 250 * } // myMutex is released when lock goes out of scope. 251 */ 252 253 class U_COMMON_API UMutex { 254 public: 255 UMUTEX_CONSTEXPR UMutex() {} 256 ~UMutex() = default; 257 258 UMutex(const UMutex &other) = delete; 259 UMutex &operator =(const UMutex &other) = delete; 260 void *operator new(size_t) = delete; 261 262 // requirements for C++ BasicLockable, allows UMutex to work with std::lock_guard 263 void lock() { 264 #ifndef __wasi__ 265 std::mutex *m = fMutex.load(std::memory_order_acquire); 266 if (m == nullptr) { m = getMutex(); } 267 m->lock(); 268 #endif 269 } 270 void unlock() { 271 #ifndef __wasi__ 272 fMutex.load(std::memory_order_relaxed)->unlock(); 273 #endif 274 } 275 276 static void cleanup(); 277 278 private: 279 #ifndef __wasi__ 280 alignas(std::mutex) char fStorage[sizeof(std::mutex)] {}; 281 std::atomic<std::mutex *> fMutex { nullptr }; 282 #endif 283 284 /** All initialized UMutexes are kept in a linked list, so that they can be found, 285 * and the underlying std::mutex destructed, by u_cleanup(). 286 */ 287 UMutex *fListLink { nullptr }; 288 static UMutex *gListHead; 289 290 /** Out-of-line function to lazily initialize a UMutex on first use. 291 * Initial fast check is inline, in lock(). The returned value may never 292 * be nullptr. 293 */ 294 #ifndef __wasi__ 295 std::mutex *getMutex(); 296 #endif 297 }; 298 299 300 /* Lock a mutex. 301 * @param mutex The given mutex to be locked. Pass NULL to specify 302 * the global ICU mutex. Recursive locks are an error 303 * and may cause a deadlock on some platforms. 304 */ 305 U_CAPI void U_EXPORT2 umtx_lock(UMutex* mutex); 306 307 /* Unlock a mutex. 308 * @param mutex The given mutex to be unlocked. Pass NULL to specify 309 * the global ICU mutex. 310 */ 311 U_CAPI void U_EXPORT2 umtx_unlock (UMutex* mutex); 312 313 314 U_NAMESPACE_END 315 316 #endif /* UMUTEX_H */ 317 /*eof*/ 318