X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/51004dcb01e06fef634b61be77ed73dd61cb6db9..a01113dcd0f39d5da295ef82785beff9ed86fe38:/icuSources/common/umutex.h diff --git a/icuSources/common/umutex.h b/icuSources/common/umutex.h old mode 100644 new mode 100755 index 893de8e6..476fcefc --- a/icuSources/common/umutex.h +++ b/icuSources/common/umutex.h @@ -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. ********************************************************************** * @@ -18,212 +20,211 @@ #ifndef UMUTEX_H #define UMUTEX_H +#include +#include +#include + #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 +#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 -#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 -#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. +// 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; + #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; #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 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 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 -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 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 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*/