#include "unicode/utypes.h"
#include "uassert.h"
+#include "ucln_cmn.h"
#include "cmemory.h"
U_NAMESPACE_BEGIN
#error U_USER_MUTEX_CPP not supported
#endif
+
/*************************************************************************************************
*
* ICU Mutex wrappers.
*
*************************************************************************************************/
-// The ICU global mutex. Used when ICU implementation code passes NULL for the mutex pointer.
-static UMutex *globalMutex() {
- static UMutex *m = STATIC_NEW(UMutex);
- return m;
+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
+
+U_CDECL_BEGIN
+static UBool U_CALLCONV umtx_cleanup() {
+ 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();
+ return true;
+}
+
+static void U_CALLCONV umtx_init() {
+ initMutex = STATIC_NEW(std::mutex);
+ initCondition = STATIC_NEW(std::condition_variable);
+ ucln_common_registerCleanup(UCLN_COMMON_MUTEX, umtx_cleanup);
+}
+U_CDECL_END
+
+
+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;
+}
+
+UMutex *UMutex::gListHead = nullptr;
+
+void UMutex::cleanup() {
+ UMutex *next = nullptr;
+ for (UMutex *m = gListHead; m != nullptr; m = next) {
+ (*m->fMutex).~mutex();
+ m->fMutex = nullptr;
+ next = m->fListLink;
+ m->fListLink = nullptr;
+ }
+ gListHead = nullptr;
}
+
U_CAPI void U_EXPORT2
umtx_lock(UMutex *mutex) {
if (mutex == nullptr) {
- mutex = globalMutex();
+ mutex = &globalMutex;
}
- mutex->fMutex.lock();
+ mutex->lock();
}
umtx_unlock(UMutex* mutex)
{
if (mutex == nullptr) {
- mutex = globalMutex();
+ mutex = &globalMutex;
}
- mutex->fMutex.unlock();
+ mutex->unlock();
}
*
*************************************************************************************************/
-static std::mutex &initMutex() {
- static std::mutex *m = STATIC_NEW(std::mutex);
- return *m;
-}
-
-static std::condition_variable &initCondition() {
- static std::condition_variable *cv = STATIC_NEW(std::condition_variable);
- return *cv;
-}
-
-
// 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.
//
U_COMMON_API UBool U_EXPORT2
umtx_initImplPreInit(UInitOnce &uio) {
- std::unique_lock<std::mutex> lock(initMutex());
-
+ std::call_once(*pInitFlag, umtx_init);
+ std::unique_lock<std::mutex> lock(*initMutex);
if (umtx_loadAcquire(uio.fState) == 0) {
umtx_storeRelease(uio.fState, 1);
return true; // Caller will next call the init function.
while (umtx_loadAcquire(uio.fState) == 1) {
// Another thread is currently running the initialization.
// Wait until it completes.
- initCondition().wait(lock);
+ initCondition->wait(lock);
}
U_ASSERT(uio.fState == 2);
return false;
U_COMMON_API void U_EXPORT2
umtx_initImplPostInit(UInitOnce &uio) {
{
- std::unique_lock<std::mutex> lock(initMutex());
+ std::unique_lock<std::mutex> lock(*initMutex);
umtx_storeRelease(uio.fState, 2);
}
- initCondition().notify_all();
+ initCondition->notify_all();
}
U_NAMESPACE_END