From: Andy Heninger Date: Sat, 29 Sep 2018 00:02:04 +0000 (-0700) Subject: ICU-10183 Mutexes, add C++11 std::mutex based implementation. X-Git-Tag: release-64-rc~248 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f71796d5e54c7b481256f664f7549ed0a27e5cc6;p=icu ICU-10183 Mutexes, add C++11 std::mutex based implementation. --- diff --git a/icu4c/source/common/putilimp.h b/icu4c/source/common/putilimp.h index d2cc37cde37..73defd9d072 100644 --- a/icu4c/source/common/putilimp.h +++ b/icu4c/source/common/putilimp.h @@ -204,7 +204,7 @@ typedef size_t uintptr_t; /** * \def U_HAVE_STD_ATOMICS - * Defines whether to use the standard C++11 functions + * Defines whether to use the C++11 std::atomic functions. * If false, ICU will fall back to compiler or platform specific alternatives. * Note: support for these fall back options for atomics will be removed in a future version * of ICU, and the use of C++ 11 atomics will be required. @@ -232,6 +232,22 @@ typedef size_t uintptr_t; # define U_HAVE_CLANG_ATOMICS 0 #endif + +/** + * \def U_HAVE_STD_MUTEX + * Defines whether to use the C++11 std::mutex functions. + * If false, ICU will fall back to compiler or platform specific alternatives. + * std::mutex is preferred, and used by default unless this setting is overridden. + * Note: support for other options for mutexes will be removed in a future version + * of ICU, and the use of std::mutex will be required. + * @internal + */ +#ifdef U_HAVE_STD_MUTEX + /* Use the predefined value. */ +#else +# define U_HAVE_STD_MUTEX 1 +#endif + /*===========================================================================*/ /** @{ Programs used by ICU code */ /*===========================================================================*/ diff --git a/icu4c/source/common/umutex.cpp b/icu4c/source/common/umutex.cpp index 29dbc90ec98..9d4fda16d7b 100644 --- a/icu4c/source/common/umutex.cpp +++ b/icu4c/source/common/umutex.cpp @@ -39,6 +39,106 @@ static UMutex globalMutex = U_MUTEX_INITIALIZER; // Build time user mutex hook: #include "U_USER_MUTEX_CPP" #include U_MUTEX_XSTR(U_USER_MUTEX_CPP) + +#elif U_HAVE_STD_MUTEX + +// C++11 std::mutex based implementation of ICU mutex wrappers. +// + +U_CAPI void U_EXPORT2 +umtx_lock(UMutex *mutex) { + if (mutex == nullptr) { + mutex = &globalMutex; + } + mutex->fMutex.lock(); +} + + +U_CAPI void U_EXPORT2 +umtx_unlock(UMutex* mutex) +{ + if (mutex == nullptr) { + mutex = &globalMutex; + } + mutex->fMutex.unlock(); +} + + +U_CAPI void U_EXPORT2 +umtx_condWait(UConditionVar *cond, UMutex *mutex) { + if (mutex == nullptr) { + mutex = &globalMutex; + } + cond->fCV.wait(mutex->fMutex); +} + + +U_CAPI void U_EXPORT2 +umtx_condBroadcast(UConditionVar *cond) { + cond->fCV.notify_all(); +} + + +U_CAPI void U_EXPORT2 +umtx_condSignal(UConditionVar *cond) { + cond->fCV.notify_one(); +} + + +U_NAMESPACE_BEGIN + +static std::mutex initMutex; +static std::condition_variable initCondition; + + +// 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) { + std::unique_lock lock(initMutex); + + if (umtx_loadAcquire(uio.fState) == 0) { + umtx_storeRelease(uio.fState, 1); + return true; // Caller will next call the init function. + } else { + 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); + 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) { + { + std::unique_lock lock(initMutex); + umtx_storeRelease(uio.fState, 2); + } + initCondition.notify_all(); +} + +U_NAMESPACE_END + +// End of std::mutex specific umutex implementation. + + + #elif U_PLATFORM_USES_ONLY_WIN32_API #if defined U_NO_PLATFORM_ATOMICS diff --git a/icu4c/source/common/umutex.h b/icu4c/source/common/umutex.h index a15e7a4fc33..86a2480873c 100644 --- a/icu4c/source/common/umutex.h +++ b/icu4c/source/common/umutex.h @@ -48,6 +48,10 @@ U_NAMESPACE_END #if defined (U_USER_ATOMICS_H) #include U_MUTEX_XSTR(U_USER_ATOMICS_H) +// TODO: There is direct use of std::atomic from number__mapper, numberrangeformatter +// Either add ICU abstraction, or deprecate U_USER_ATOMICS_H. +// See Jira issu ICU-20185. + #elif U_HAVE_STD_ATOMICS // C++11 atomics are available. @@ -328,8 +332,6 @@ U_NAMESPACE_END /************************************************************************************************* * * Mutex Definitions. Platform Dependent, #if platform chain follows. - * TODO: Add a C++11 version. - * Need to convert all mutex using files to C++ first. * *************************************************************************************************/ @@ -337,6 +339,25 @@ U_NAMESPACE_END // #include "U_USER_MUTEX_H" #include U_MUTEX_XSTR(U_USER_MUTEX_H) +#elif U_HAVE_STD_MUTEX + +#include +#include + + +struct UMutex { + std::mutex fMutex; +}; + +struct UConditionVar { + std::condition_variable_any fCV; +}; + +#define U_MUTEX_INITIALIZER {} +#define U_CONDITION_INITIALIZER {} + + + #elif U_PLATFORM_USES_ONLY_WIN32_API /* For CRITICAL_SECTION */