]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/common/umutex.h
ICU-64260.0.1.tar.gz
[apple/icu.git] / icuSources / common / umutex.h
old mode 100644 (file)
new mode 100755 (executable)
index 893de8e..476fcef
@@ -1,6 +1,8 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /*
 **********************************************************************
-*   Copyright (C) 1997-2012, International Business Machines
+*   Copyright (C) 1997-2015, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 **********************************************************************
 *
 #ifndef UMUTEX_H
 #define UMUTEX_H
 
+#include <atomic>
+#include <condition_variable>
+#include <mutex>
+
 #include "unicode/utypes.h"
 #include "unicode/uclean.h"
+#include "unicode/uobject.h"
+
 #include "putilimp.h"
 
-/* For _ReadWriteBarrier(). */
-#if defined(_MSC_VER) && _MSC_VER >= 1500
-# include <intrin.h>
+#if defined(U_USER_ATOMICS_H) || defined(U_USER_MUTEX_H)
+// Support for including an alternate implementation of atomic & mutex operations has been withdrawn.
+// See issue ICU-20185.
+#error U_USER_ATOMICS and U_USER_MUTEX_H are not supported
 #endif
 
-/* For CRITICAL_SECTION */
-#if U_PLATFORM_HAS_WIN32_API
-#if 0  
-/* TODO(andy): Why doesn't windows.h compile in all files? It does in some.
- *             The intent was to include windows.h here, and have struct UMutex
- *             have an embedded CRITICAL_SECTION when building on Windows.
- *             The workaround is to put some char[] storage in UMutex instead,
- *             avoiding the need to include windows.h everwhere this header is included.
- */
-# define WIN32_LEAN_AND_MEAN
-# define VC_EXTRALEAN
-# define NOUSER
-# define NOSERVICE
-# define NOIME
-# define NOMCX
-# include <windows.h>
-#endif  /* 0 */
-#define U_WINDOWS_CRIT_SEC_SIZE 64
-#endif  /* win32 */
-
-#if U_PLATFORM_IS_DARWIN_BASED
-#if defined(__STRICT_ANSI__)
-#define UPRV_REMAP_INLINE
-#define inline
-#endif
-#include <libkern/OSAtomic.h>
-#define USE_MAC_OS_ATOMIC_INCREMENT 1
-#if defined(UPRV_REMAP_INLINE)
-#undef inline
-#undef UPRV_REMAP_INLINE
-#endif
-#endif
 
-/*
- * If we do not compile with dynamic_annotations.h then define
- * empty annotation macros.
- *  See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations
- */
-#ifndef ANNOTATE_HAPPENS_BEFORE
-# define ANNOTATE_HAPPENS_BEFORE(obj)
-# define ANNOTATE_HAPPENS_AFTER(obj)
-# define ANNOTATE_UNPROTECTED_READ(x) (x)
+// Export an explicit template instantiation of std::atomic<int32_t>. 
+// When building DLLs for Windows this is required as it is used as a data member of the exported SharedObject class.
+// See digitlst.h, pluralaffix.h, datefmt.h, and others for similar examples.
+#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN && !defined(U_IN_DOXYGEN)
+#if defined(__clang__) || defined(_MSC_VER)
+  #if defined(__clang__)
+    // Suppress the warning that the explicit instantiation after explicit specialization has no effect.
+    #pragma clang diagnostic push
+    #pragma clang diagnostic ignored "-Winstantiation-after-specialization"
+  #endif
+template struct U_COMMON_API std::atomic<int32_t>;
+  #if defined(__clang__)
+    #pragma clang diagnostic pop
+  #endif
+#elif defined(__GNUC__)
+// For GCC this class is already exported/visible, so no need for U_COMMON_API.
+template struct std::atomic<int32_t>;
 #endif
-
-#ifndef UMTX_FULL_BARRIER
-# if U_HAVE_GCC_ATOMICS
-#  define UMTX_FULL_BARRIER __sync_synchronize();
-# elif defined(_MSC_VER) && _MSC_VER >= 1500
-    /* From MSVC intrin.h. Use _ReadWriteBarrier() only on MSVC 9 and higher. */
-#  define UMTX_FULL_BARRIER _ReadWriteBarrier();
-# elif U_PLATFORM_IS_DARWIN_BASED
-#  define UMTX_FULL_BARRIER OSMemoryBarrier();
-# else
-#  define UMTX_FULL_BARRIER \
-    { \
-        umtx_lock(NULL); \
-        umtx_unlock(NULL); \
-    }
-# endif
 #endif
 
-#ifndef UMTX_ACQUIRE_BARRIER
-# define UMTX_ACQUIRE_BARRIER UMTX_FULL_BARRIER
-#endif
 
-#ifndef UMTX_RELEASE_BARRIER
-# define UMTX_RELEASE_BARRIER UMTX_FULL_BARRIER
-#endif
+U_NAMESPACE_BEGIN
 
-/**
- * \def UMTX_CHECK
- * Encapsulates a safe check of an expression
- * for use with double-checked lazy inititialization.
- * Either memory barriers or mutexes are required, to prevent both the hardware
- * and the compiler from reordering operations across the check.
- * The expression must involve only a  _single_ variable, typically
- *    a possibly null pointer or a boolean that indicates whether some service
- *    is initialized or not.
- * The setting of the variable involved in the test must be the last step of
- *    the initialization process.
+/****************************************************************************
  *
- * @internal
- */
-#define UMTX_CHECK(pMutex, expression, result) \
-    { \
-        (result)=(expression); \
-        UMTX_ACQUIRE_BARRIER; \
-    }
-/*
- * TODO: Replace all uses of UMTX_CHECK and surrounding code
- * with SimpleSingleton or TriStateSingleton, and remove UMTX_CHECK.
- */
-
-/*
- * Code within ICU that accesses shared static or global data should
- * instantiate a Mutex object while doing so.  The unnamed global mutex
- * is used throughout ICU, so keep locking short and sweet.
+ *   Low Level Atomic Operations, ICU wrappers for.
  *
- * For example:
+ ****************************************************************************/
+
+typedef std::atomic<int32_t> u_atomic_int32_t;
+#define ATOMIC_INT32_T_INITIALIZER(val) ATOMIC_VAR_INIT(val)
+
+inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
+    return var.load(std::memory_order_acquire);
+}
+
+inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
+    var.store(val, std::memory_order_release);
+}
+
+inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
+    return var->fetch_add(1) + 1;
+}
+
+inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
+    return var->fetch_sub(1) - 1;
+}
+
+
+/*************************************************************************************************
  *
- * void Function(int arg1, int arg2)
- * {
- *   static Object* foo;     // Shared read-write object
- *   umtx_lock(NULL);        // Lock the ICU global mutex
- *   foo->Method();
- *   umtx_unlock(NULL);
- * }
+ *  UInitOnce Definitions.
  *
- * an alternative C++ mutex API is defined in the file common/mutex.h
- */
+ *************************************************************************************************/
+
+struct UInitOnce {
+    u_atomic_int32_t   fState;
+    UErrorCode       fErrCode;
+    void reset() {fState = 0;}
+    UBool isReset() {return umtx_loadAcquire(fState) == 0;}
+// Note: isReset() is used by service registration code.
+//                 Thread safety of this usage needs review.
+};
 
-/*
- * UMutex - Mutexes for use by ICU implementation code.
- *          Must be declared as static or globals. They cannot appear as members
- *          of other objects.
- *          UMutex structs must be initialized.
- *          Example:
- *            static UMutex = U_MUTEX_INITIALIZER;
- *          The declaration of struct UMutex is platform dependent.
- */
+#define U_INITONCE_INITIALIZER {ATOMIC_INT32_T_INITIALIZER(0), U_ZERO_ERROR}
 
 
-#if U_PLATFORM_HAS_WIN32_API
+U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &);
+U_COMMON_API void  U_EXPORT2 umtx_initImplPostInit(UInitOnce &);
 
-/*  U_INIT_ONCE mimics the windows API INIT_ONCE, which exists on Windows Vista and newer.
- *  When ICU no longer needs to support older Windows platforms (XP) that do not have
- * a native INIT_ONCE, switch this implementation over to wrap the native Windows APIs.
- */
-typedef struct U_INIT_ONCE {
-    long               fState;
-    void              *fContext;
-} U_INIT_ONCE;
-#define U_INIT_ONCE_STATIC_INIT {0, NULL}
-
-typedef struct UMutex {
-    U_INIT_ONCE       fInitOnce;
-    UMTX              fUserMutex;
-    UBool             fInitialized;  /* Applies to fUserMutex only. */
-    /* CRITICAL_SECTION  fCS; */  /* See note above. Unresolved problems with including
-                                   * Windows.h, which would allow using CRITICAL_SECTION
-                                   * directly here. */
-    char              fCS[U_WINDOWS_CRIT_SEC_SIZE];
-} UMutex;
-
-/* Initializer for a static UMUTEX. Deliberately contains no value for the
- *  CRITICAL_SECTION.
- */
-#define U_MUTEX_INITIALIZER {U_INIT_ONCE_STATIC_INIT, NULL, FALSE}
+template<class T> void umtx_initOnce(UInitOnce &uio, T *obj, void (U_CALLCONV T::*fp)()) {
+    if (umtx_loadAcquire(uio.fState) == 2) {
+        return;
+    }
+    if (umtx_initImplPreInit(uio)) {
+        (obj->*fp)();
+        umtx_initImplPostInit(uio);
+    }
+}
 
-#elif U_PLATFORM_IMPLEMENTS_POSIX
-#include <pthread.h>
 
-struct UMutex {
-    pthread_mutex_t  fMutex;
-    UMTX             fUserMutex;
-    UBool            fInitialized;
-};
-#define U_MUTEX_INITIALIZER  {PTHREAD_MUTEX_INITIALIZER, NULL, FALSE}
+// umtx_initOnce variant for plain functions, or static class functions.
+//               No context parameter.
+inline void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)()) {
+    if (umtx_loadAcquire(uio.fState) == 2) {
+        return;
+    }
+    if (umtx_initImplPreInit(uio)) {
+        (*fp)();
+        umtx_initImplPostInit(uio);
+    }
+}
 
-#else
-/* Unknow platform type. */
-struct UMutex {
-    void *fMutex;
-};
-#define U_MUTEX_INITIALIZER {NULL}
-#error Unknown Platform.
+// umtx_initOnce variant for plain functions, or static class functions.
+//               With ErrorCode, No context parameter.
+inline void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(UErrorCode &), UErrorCode &errCode) {
+    if (U_FAILURE(errCode)) {
+        return;
+    }
+    if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) {
+        // We run the initialization.
+        (*fp)(errCode);
+        uio.fErrCode = errCode;
+        umtx_initImplPostInit(uio);
+    } else {
+        // Someone else already ran the initialization.
+        if (U_FAILURE(uio.fErrCode)) {
+            errCode = uio.fErrCode;
+        }
+    }
+}
 
-#endif
+// umtx_initOnce variant for plain functions, or static class functions,
+//               with a context parameter.
+template<class T> void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(T), T context) {
+    if (umtx_loadAcquire(uio.fState) == 2) {
+        return;
+    }
+    if (umtx_initImplPreInit(uio)) {
+        (*fp)(context);
+        umtx_initImplPostInit(uio);
+    }
+}
+
+// umtx_initOnce variant for plain functions, or static class functions,
+//               with a context parameter and an error code.
+template<class T> void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(T, UErrorCode &), T context, UErrorCode &errCode) {
+    if (U_FAILURE(errCode)) {
+        return;
+    }
+    if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) {
+        // We run the initialization.
+        (*fp)(context, errCode);
+        uio.fErrCode = errCode;
+        umtx_initImplPostInit(uio);
+    } else {
+        // Someone else already ran the initialization.
+        if (U_FAILURE(uio.fErrCode)) {
+            errCode = uio.fErrCode;
+        }
+    }
+}
+
+
+/**
+ * ICU Mutex wrappers.  Originally wrapped operating system mutexes, giving the rest of ICU a
+ * platform independent set of mutex operations.  Now vestigial, wrapping std::mutex only.
+ * For internal ICU use only.
+ *
+ * Caution: do not directly declare static or global instances of UMutex. Doing so can introduce
+ * static initializers, which are disallowed in ICU library code. Instead, use the following
+ * idiom, which avoids static init and also avoids ordering issues on destruction
+ * (use after delete) by avoiding destruction altogether.
+ *
+ *  UMutex *myMutex() {
+ *      static UMutex *m = STATIC_NEW(UMutex);
+ *      return m;
+ *  }
+ *  ...
+ *
+ *  Mutex lock(myMutex());   // hold myMutex until the variable "lock" goes out of scope.
+ */
+
+struct UMutex : public icu::UMemory {
+    UMutex() = default;
+    ~UMutex() = default;
+    UMutex(const UMutex &other) = delete;
+    UMutex &operator =(const UMutex &other) = delete;
+
+    std::mutex   fMutex = {};    // Note: struct - pubic members - because most access is from
+    //                           //       plain C style functions (umtx_lock(), etc.)
+};
 
-#if (U_PLATFORM != U_PF_CYGWIN && U_PLATFORM != U_PF_MINGW) || defined(CYGWINMSVC)
-typedef struct UMutex UMutex;
-#endif
-    
 /* Lock a mutex.
  * @param mutex The given mutex to be locked.  Pass NULL to specify
  *              the global ICU mutex.  Recursive locks are an error
  *              and may cause a deadlock on some platforms.
  */
-U_CAPI void U_EXPORT2 umtx_lock(UMutex* mutex); 
+U_INTERNAL void U_EXPORT2 umtx_lock(UMutex* mutex);
 
 /* Unlock a mutex.
  * @param mutex The given mutex to be unlocked.  Pass NULL to specify
  *              the global ICU mutex.
  */
-U_CAPI void U_EXPORT2 umtx_unlock (UMutex* mutex);
+U_INTERNAL void U_EXPORT2 umtx_unlock (UMutex* mutex);
 
-/*
- * Atomic Increment and Decrement of an int32_t value.
- *
- * Return Values:
- *   If the result of the operation is zero, the return zero.
- *   If the result of the operation is not zero, the sign of returned value
- *      is the same as the sign of the result, but the returned value itself may
- *      be different from the result of the operation.
- */
-U_CAPI int32_t U_EXPORT2 umtx_atomic_inc(int32_t *);
-U_CAPI int32_t U_EXPORT2 umtx_atomic_dec(int32_t *);
 
-#endif /*_CMUTEX*/
+U_NAMESPACE_END
+
+#endif /* UMUTEX_H */
 /*eof*/