]> granicus.if.org Git - icu/commitdiff
ICU-10183 Mutexes, add C++11 std::mutex based implementation.
authorAndy Heninger <andy.heninger@gmail.com>
Sat, 29 Sep 2018 00:02:04 +0000 (17:02 -0700)
committerAndy Heninger <andy.heninger@gmail.com>
Wed, 31 Oct 2018 20:07:19 +0000 (13:07 -0700)
icu4c/source/common/putilimp.h
icu4c/source/common/umutex.cpp
icu4c/source/common/umutex.h

index d2cc37cde37833045e8ccdfb0df54a15c5b93d3e..73defd9d072809459ee8693f221ad4a6928e2999 100644 (file)
@@ -204,7 +204,7 @@ typedef size_t uintptr_t;
 
 /**
  * \def U_HAVE_STD_ATOMICS
- * Defines whether to use the standard C++11 <atomic> 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                                             */
 /*===========================================================================*/
index 29dbc90ec9854d21487e472f5f0c3f32184ba684..9d4fda16d7b3d5118c8d7e85299566e999b7c426 100644 (file)
@@ -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<std::mutex> 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<std::mutex> 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
index a15e7a4fc338d1239112f68d6155d9e5ec256f46..86a2480873c9814e9f9948fb3481dc66bf4dc96c 100644 (file)
@@ -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<type *> 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 <mutex>
+#include <condition_variable>
+
+
+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 */