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