X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/46f4442e9a5a4f3b98b7c1083586332f6a8a99a4..1a147d096ae81f4c8262f7bfc56bd19fc2dee932:/icuSources/common/umutex.h diff --git a/icuSources/common/umutex.h b/icuSources/common/umutex.h old mode 100644 new mode 100755 index e02ae336..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-2006, International Business Machines +* Copyright (C) 1997-2015, International Business Machines * Corporation and others. All Rights Reserved. ********************************************************************** * @@ -18,126 +20,211 @@ #ifndef UMUTEX_H #define UMUTEX_H +#include +#include +#include + #include "unicode/utypes.h" -#include "unicode/uclean.h" +#include "unicode/uclean.h" +#include "unicode/uobject.h" +#include "putilimp.h" -/* APP_NO_THREADS is an old symbol. We'll honour it if present. */ -#ifdef APP_NO_THREADS -# define ICU_USE_THREADS 0 +#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 -/* ICU_USE_THREADS - * - * Allows thread support (use of mutexes) to be compiled out of ICU. - * Default: use threads. - * Even with thread support compiled out, applications may override the - * (empty) mutex implementation with the u_setMutexFunctions() functions. - */ -#ifndef ICU_USE_THREADS -# define ICU_USE_THREADS 1 -#endif -/** - * By default assume that we are on a machine with a weak memory model, - * and the double check lock won't work reliably. - */ -#if !defined(UMTX_STRONG_MEMORY_MODEL) -#define UMTX_STRONG_MEMORY_MODEL 0 +// 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 #endif -/** - * \def UMTX_CHECK - * Encapsulates a safe check for an expression (usually a condition) - * for lazy variable inititialization. - * On CPUs with weak memory models, this must use memory fence instructions - * or mutexes. - * @internal - */ -#if UMTX_STRONG_MEMORY_MODEL -#define UMTX_CHECK(pMutex, expression, result) \ - (result)=(expression) +U_NAMESPACE_BEGIN -#else +/**************************************************************************** + * + * Low Level Atomic Operations, ICU wrappers for. + * + ****************************************************************************/ -#define UMTX_CHECK(pMutex, expression, result) \ - umtx_lock(pMutex); \ - (result)=(expression); \ - umtx_unlock(pMutex) +typedef std::atomic u_atomic_int32_t; +#define ATOMIC_INT32_T_INITIALIZER(val) ATOMIC_VAR_INIT(val) -#endif +inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { + return var.load(std::memory_order_acquire); +} -/* - * 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. +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; +} + + +/************************************************************************************************* * - * For example: + * UInitOnce Definitions. * - * 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); - * } + *************************************************************************************************/ + +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. +}; + +#define U_INITONCE_INITIALIZER {ATOMIC_INT32_T_INITIALIZER(0), U_ZERO_ERROR} + + +U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &); +U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &); + +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); + } +} + + +// 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); + } +} + +// 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; + } + } +} + +// 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; + * } + * ... * - * an alternative C++ mutex API is defined in the file common/mutex.h + * Mutex lock(myMutex()); // hold myMutex until the variable "lock" goes out of scope. */ -/* Lock a mutex. +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.) +}; + +/* 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 ( UMTX* mutex ); +U_INTERNAL void U_EXPORT2 umtx_lock(UMutex* mutex); -/* Unlock a mutex. Pass in NULL if you want the single global - 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 ( UMTX* mutex ); - -/* Initialize a mutex. Use it this way: - umtx_init( &aMutex ); - * ICU Mutexes do not need explicit initialization before use. Use of this - * function is not necessary. - * Initialization of an already initialized mutex has no effect, and is safe to do. - * Initialization of mutexes is thread safe. Two threads can concurrently - * initialize the same mutex without causing problems. - * @param mutex The given mutex to be initialized - */ -U_CAPI void U_EXPORT2 umtx_init ( UMTX* mutex ); - -/* Destroy a mutex. This will free the resources of a mutex. - * Use it this way: - * umtx_destroy( &aMutex ); - * Destroying an already destroyed mutex has no effect, and causes no problems. - * This function is not thread safe. Two threads must not attempt to concurrently - * destroy the same mutex. - * @param mutex The given mutex to be destroyed. - */ -U_CAPI void U_EXPORT2 umtx_destroy( UMTX *mutex ); +U_INTERNAL void U_EXPORT2 umtx_unlock (UMutex* mutex); +U_NAMESPACE_END -/* - * 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*/ +#endif /* UMUTEX_H */ /*eof*/ - - -