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 30U_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 46namespace { 47std::mutex *initMutex; 48std::condition_variable *initCondition; 49 50// The ICU global mutex. 51// Used when ICU implementation code passes nullptr for the mutex pointer. 52UMutex globalMutex; 53 54std::once_flag initFlag; 55std::once_flag *pInitFlag = &initFlag; 56 57} // Anonymous namespace 58 59U_CDECL_BEGIN 60static UBool U_CALLCONV umtx_cleanup() { 61 initMutex->~mutex(); 62 initCondition->~condition_variable(); 63 UMutex::cleanup(); 64 65 // Reset the once_flag, by destructing it and creating a fresh one in its place. 66 // Do not use this trick anywhere else in ICU; use umtx_initOnce, not std::call_once(). 67 pInitFlag->~once_flag(); 68 pInitFlag = new(&initFlag) std::once_flag(); 69 return true; 70} 71 72static void U_CALLCONV umtx_init() { 73 initMutex = STATIC_NEW(std::mutex); 74 initCondition = STATIC_NEW(std::condition_variable); 75 ucln_common_registerCleanup(UCLN_COMMON_MUTEX, umtx_cleanup); 76} 77U_CDECL_END 78 79 80std::mutex *UMutex::getMutex() { 81 std::mutex *retPtr = fMutex.load(std::memory_order_acquire); 82 if (retPtr == nullptr) { 83 std::call_once(*pInitFlag, umtx_init); 84 std::lock_guard<std::mutex> guard(*initMutex); 85 retPtr = fMutex.load(std::memory_order_acquire); 86 if (retPtr == nullptr) { 87 fMutex = new(fStorage) std::mutex(); 88 retPtr = fMutex; 89 fListLink = gListHead; 90 gListHead = this; 91 } 92 } 93 U_ASSERT(retPtr != nullptr); 94 return retPtr; 95} 96 97UMutex *UMutex::gListHead = nullptr; 98 99void UMutex::cleanup() { 100 UMutex *next = nullptr; 101 for (UMutex *m = gListHead; m != nullptr; m = next) { 102 (*m->fMutex).~mutex(); 103 m->fMutex = nullptr; 104 next = m->fListLink; 105 m->fListLink = nullptr; 106 } 107 gListHead = nullptr; 108} 109 110 111U_CAPI void U_EXPORT2 112umtx_lock(UMutex *mutex) { 113 if (mutex == nullptr) { 114 mutex = &globalMutex; 115 } 116 mutex->lock(); 117} 118 119 120U_CAPI void U_EXPORT2 121umtx_unlock(UMutex* mutex) 122{ 123 if (mutex == nullptr) { 124 mutex = &globalMutex; 125 } 126 mutex->unlock(); 127} 128 129 130/************************************************************************************************* 131 * 132 * UInitOnce Implementation 133 * 134 *************************************************************************************************/ 135 136// This function is called when a test of a UInitOnce::fState reveals that 137// initialization has not completed, that we either need to call the init 138// function on this thread, or wait for some other thread to complete. 139// 140// The actual call to the init function is made inline by template code 141// that knows the C++ types involved. This function returns true if 142// the caller needs to call the Init function. 143// 144U_COMMON_API UBool U_EXPORT2 145umtx_initImplPreInit(UInitOnce &uio) { 146 std::call_once(*pInitFlag, umtx_init); 147 std::unique_lock<std::mutex> lock(*initMutex); 148 if (umtx_loadAcquire(uio.fState) == 0) { 149 umtx_storeRelease(uio.fState, 1); 150 return true; // Caller will next call the init function. 151 } else { 152 while (umtx_loadAcquire(uio.fState) == 1) { 153 // Another thread is currently running the initialization. 154 // Wait until it completes. 155 initCondition->wait(lock); 156 } 157 U_ASSERT(uio.fState == 2); 158 return false; 159 } 160} 161 162 163// This function is called by the thread that ran an initialization function, 164// just after completing the function. 165// Some threads may be waiting on the condition, requiring the broadcast wakeup. 166// Some threads may be racing to test the fState variable outside of the mutex, 167// requiring the use of store/release when changing its value. 168 169U_COMMON_API void U_EXPORT2 170umtx_initImplPostInit(UInitOnce &uio) { 171 { 172 std::unique_lock<std::mutex> lock(*initMutex); 173 umtx_storeRelease(uio.fState, 2); 174 } 175 initCondition->notify_all(); 176} 177 178U_NAMESPACE_END 179 180/************************************************************************************************* 181 * 182 * Deprecated functions for setting user mutexes. 183 * 184 *************************************************************************************************/ 185 186U_DEPRECATED void U_EXPORT2 187u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *, 188 UMtxFn *, UMtxFn *, UErrorCode *status) { 189 if (U_SUCCESS(*status)) { 190 *status = U_UNSUPPORTED_ERROR; 191 } 192 return; 193} 194 195 196 197U_DEPRECATED void U_EXPORT2 198u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *, 199 UErrorCode *status) { 200 if (U_SUCCESS(*status)) { 201 *status = U_UNSUPPORTED_ERROR; 202 } 203 return; 204} 205