umutex.cpp (6182B)
1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ****************************************************************************** 5 * 6 * Copyright (C) 1997-2016, International Business Machines 7 * Corporation and others. All Rights Reserved. 8 * 9 ****************************************************************************** 10 * 11 * File umutex.cpp 12 * 13 * Modification History: 14 * 15 * Date Name Description 16 * 04/02/97 aliu Creation. 17 * 04/07/99 srl updated 18 * 05/13/99 stephen Changed to umutex (from cmutex). 19 * 11/22/99 aliu Make non-global mutex autoinitialize [j151] 20 ****************************************************************************** 21 */ 22 23 #include "umutex.h" 24 25 #include "unicode/utypes.h" 26 #include "uassert.h" 27 #include "ucln_cmn.h" 28 #include "cmemory.h" 29 30 U_NAMESPACE_BEGIN 31 32 33 #if defined(U_USER_MUTEX_CPP) 34 // Support for including an alternate implementation of mutexes has been withdrawn. 35 // See issue ICU-20185. 36 #error U_USER_MUTEX_CPP not supported 37 #endif 38 39 40 /************************************************************************************************* 41 * 42 * ICU Mutex wrappers. 43 * 44 *************************************************************************************************/ 45 46 #ifndef __wasi__ 47 namespace { 48 std::mutex *initMutex; 49 std::condition_variable *initCondition; 50 51 // The ICU global mutex. 52 // Used when ICU implementation code passes nullptr for the mutex pointer. 53 UMutex globalMutex; 54 55 std::once_flag initFlag; 56 std::once_flag *pInitFlag = &initFlag; 57 58 } // Anonymous namespace 59 #endif 60 61 U_CDECL_BEGIN 62 static UBool U_CALLCONV umtx_cleanup() { 63 #ifndef __wasi__ 64 initMutex->~mutex(); 65 initCondition->~condition_variable(); 66 UMutex::cleanup(); 67 68 // Reset the once_flag, by destructing it and creating a fresh one in its place. 69 // Do not use this trick anywhere else in ICU; use umtx_initOnce, not std::call_once(). 70 pInitFlag->~once_flag(); 71 pInitFlag = new(&initFlag) std::once_flag(); 72 #endif 73 return true; 74 } 75 76 static void U_CALLCONV umtx_init() { 77 #ifndef __wasi__ 78 initMutex = STATIC_NEW(std::mutex); 79 initCondition = STATIC_NEW(std::condition_variable); 80 ucln_common_registerCleanup(UCLN_COMMON_MUTEX, umtx_cleanup); 81 #endif 82 } 83 U_CDECL_END 84 85 86 #ifndef __wasi__ 87 std::mutex *UMutex::getMutex() { 88 std::mutex *retPtr = fMutex.load(std::memory_order_acquire); 89 if (retPtr == nullptr) { 90 std::call_once(*pInitFlag, umtx_init); 91 std::lock_guard<std::mutex> guard(*initMutex); 92 retPtr = fMutex.load(std::memory_order_acquire); 93 if (retPtr == nullptr) { 94 fMutex = new(fStorage) std::mutex(); 95 retPtr = fMutex; 96 fListLink = gListHead; 97 gListHead = this; 98 } 99 } 100 U_ASSERT(retPtr != nullptr); 101 return retPtr; 102 } 103 #endif 104 105 UMutex *UMutex::gListHead = nullptr; 106 107 void UMutex::cleanup() { 108 UMutex *next = nullptr; 109 for (UMutex *m = gListHead; m != nullptr; m = next) { 110 #ifndef __wasi__ 111 (*m->fMutex).~mutex(); 112 m->fMutex = nullptr; 113 #endif 114 next = m->fListLink; 115 m->fListLink = nullptr; 116 } 117 gListHead = nullptr; 118 } 119 120 121 U_CAPI void U_EXPORT2 122 umtx_lock(UMutex *mutex) { 123 #ifndef __wasi__ 124 if (mutex == nullptr) { 125 mutex = &globalMutex; 126 } 127 mutex->lock(); 128 #endif 129 } 130 131 132 U_CAPI void U_EXPORT2 133 umtx_unlock(UMutex* mutex) 134 { 135 #ifndef __wasi__ 136 if (mutex == nullptr) { 137 mutex = &globalMutex; 138 } 139 mutex->unlock(); 140 #endif 141 } 142 143 144 /************************************************************************************************* 145 * 146 * UInitOnce Implementation 147 * 148 *************************************************************************************************/ 149 150 // This function is called when a test of a UInitOnce::fState reveals that 151 // initialization has not completed, that we either need to call the init 152 // function on this thread, or wait for some other thread to complete. 153 // 154 // The actual call to the init function is made inline by template code 155 // that knows the C++ types involved. This function returns true if 156 // the caller needs to call the Init function. 157 // 158 U_COMMON_API UBool U_EXPORT2 159 umtx_initImplPreInit(UInitOnce &uio) { 160 #ifndef __wasi__ 161 std::call_once(*pInitFlag, umtx_init); 162 std::unique_lock<std::mutex> lock(*initMutex); 163 #endif 164 if (umtx_loadAcquire(uio.fState) == 0) { 165 umtx_storeRelease(uio.fState, 1); 166 return true; // Caller will next call the init function. 167 } else { 168 #ifndef __wasi__ 169 while (umtx_loadAcquire(uio.fState) == 1) { 170 // Another thread is currently running the initialization. 171 // Wait until it completes. 172 initCondition->wait(lock); 173 } 174 U_ASSERT(uio.fState == 2); 175 #endif 176 return false; 177 } 178 } 179 180 181 // This function is called by the thread that ran an initialization function, 182 // just after completing the function. 183 // Some threads may be waiting on the condition, requiring the broadcast wakeup. 184 // Some threads may be racing to test the fState variable outside of the mutex, 185 // requiring the use of store/release when changing its value. 186 187 U_COMMON_API void U_EXPORT2 188 umtx_initImplPostInit(UInitOnce &uio) { 189 #ifndef __wasi__ 190 { 191 std::unique_lock<std::mutex> lock(*initMutex); 192 umtx_storeRelease(uio.fState, 2); 193 } 194 initCondition->notify_all(); 195 #endif 196 } 197 198 U_NAMESPACE_END 199 200 /************************************************************************************************* 201 * 202 * Deprecated functions for setting user mutexes. 203 * 204 *************************************************************************************************/ 205 206 U_DEPRECATED void U_EXPORT2 207 u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *, 208 UMtxFn *, UMtxFn *, UErrorCode *status) { 209 if (U_SUCCESS(*status)) { 210 *status = U_UNSUPPORTED_ERROR; 211 } 212 } 213 214 215 216 U_DEPRECATED void U_EXPORT2 217 u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *, 218 UErrorCode *status) { 219 if (U_SUCCESS(*status)) { 220 *status = U_UNSUPPORTED_ERROR; 221 } 222 }