X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/374ca955a76ecab1204ca8bfa63ff9238d998416..9f1b115531acc2f3640893f76e8bcf6680033062:/icuSources/common/umutex.h diff --git a/icuSources/common/umutex.h b/icuSources/common/umutex.h index 6a4fcb8f..8e1f6e93 100644 --- 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-2004, International Business Machines +* Copyright (C) 1997-2015, International Business Machines * Corporation and others. All Rights Reserved. ********************************************************************** * @@ -19,117 +21,431 @@ #define UMUTEX_H #include "unicode/utypes.h" -#include "unicode/uclean.h" +#include "unicode/uclean.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 -#endif -/* ICU_USE_THREADS +// Forward Declarations. UMutex is not in the ICU namespace (yet) because +// there are some remaining references from plain C. +struct UMutex; +struct UConditionVar; + +U_NAMESPACE_BEGIN +struct UInitOnce; +U_NAMESPACE_END + +// Stringify macros, to allow #include of user supplied atomic & mutex files. +#define U_MUTEX_STR(s) #s +#define U_MUTEX_XSTR(s) U_MUTEX_STR(s) + +/**************************************************************************** * - * 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 + * Low Level Atomic Operations. + * Compiler dependent. Not operating system dependent. + * + ****************************************************************************/ +#if defined (U_USER_ATOMICS_H) +#include U_MUTEX_XSTR(U_USER_ATOMICS_H) + +#elif U_HAVE_STD_ATOMICS + +// C++11 atomics are available. + +#include + +U_NAMESPACE_BEGIN + +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; +} +U_NAMESPACE_END + +#elif U_PLATFORM_HAS_WIN32_API + +// MSVC compiler. Reads and writes of volatile variables have +// acquire and release memory semantics, respectively. +// This is a Microsoft extension, not standard C++ behavior. +// +// Update: can't use this because of MinGW, built with gcc. +// Original plan was to use gcc atomics for MinGW, but they +// aren't supported, so we fold MinGW into this path. + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN #endif +# define VC_EXTRALEAN +# define NOUSER +# define NOSERVICE +# define NOIME +# define NOMCX +# ifndef NOMINMAX +# define NOMINMAX +# endif +# include + +U_NAMESPACE_BEGIN +typedef volatile LONG u_atomic_int32_t; +#define ATOMIC_INT32_T_INITIALIZER(val) val + +inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { + return InterlockedCompareExchange(&var, 0, 0); +} + +inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { + InterlockedExchange(&var, val); +} + -/** - * \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 +inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) { + return InterlockedIncrement(var); +} + +inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) { + return InterlockedDecrement(var); +} +U_NAMESPACE_END + + +#elif U_HAVE_CLANG_ATOMICS +/* + * Clang __c11 atomic built-ins */ -#if UMTX_STRONG_MEMORY_MODEL -#define UMTX_CHECK(pMutex, expression, result) \ - (result)=(expression); +U_NAMESPACE_BEGIN +typedef _Atomic(int32_t) u_atomic_int32_t; +#define ATOMIC_INT32_T_INITIALIZER(val) val -#else +inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { + return __c11_atomic_load(&var, __ATOMIC_ACQUIRE); +} -#define UMTX_CHECK(pMutex, expression, result) \ - umtx_lock(pMutex); \ - (result)=(expression); \ - umtx_unlock(pMutex); +inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { + return __c11_atomic_store(&var, val, __ATOMIC_RELEASE); +} -#endif +inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) { + return __c11_atomic_fetch_add(var, 1, __ATOMIC_SEQ_CST) + 1; +} + +inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) { + return __c11_atomic_fetch_sub(var, 1, __ATOMIC_SEQ_CST) - 1; +} +U_NAMESPACE_END + + +#elif U_HAVE_GCC_ATOMICS +/* + * gcc atomic ops. These are available on several other compilers as well. + */ + +U_NAMESPACE_BEGIN +typedef int32_t u_atomic_int32_t; +#define ATOMIC_INT32_T_INITIALIZER(val) val + +inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { + int32_t val = var; + __sync_synchronize(); + return val; +} + +inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { + __sync_synchronize(); + var = val; +} + +inline int32_t umtx_atomic_inc(u_atomic_int32_t *p) { + return __sync_add_and_fetch(p, 1); +} + +inline int32_t umtx_atomic_dec(u_atomic_int32_t *p) { + return __sync_sub_and_fetch(p, 1); +} +U_NAMESPACE_END + +#else /* - * 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. + * Unknown Platform. Use out-of-line functions, which in turn use mutexes. + * Slow but correct. + */ + +#define U_NO_PLATFORM_ATOMICS + +U_NAMESPACE_BEGIN +typedef int32_t u_atomic_int32_t; +#define ATOMIC_INT32_T_INITIALIZER(val) val + +U_COMMON_API int32_t U_EXPORT2 +umtx_loadAcquire(u_atomic_int32_t &var); + +U_COMMON_API void U_EXPORT2 +umtx_storeRelease(u_atomic_int32_t &var, int32_t val); + +U_COMMON_API int32_t U_EXPORT2 +umtx_atomic_inc(u_atomic_int32_t *p); + +U_COMMON_API int32_t U_EXPORT2 +umtx_atomic_dec(u_atomic_int32_t *p); + +U_NAMESPACE_END + +#endif /* Low Level Atomic Ops Platfrom Chain */ + + + +/************************************************************************************************* * - * For example: + * UInitOnce Definitions. + * These are platform neutral. * - * 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); - * } + *************************************************************************************************/ + +U_NAMESPACE_BEGIN + +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; + } + } +} + +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. * - * an alternative C++ mutex API is defined in the file common/mutex.h + *************************************************************************************************/ + +#if defined(U_USER_MUTEX_H) +// #inlcude "U_USER_MUTEX_H" +#include U_MUTEX_XSTR(U_USER_MUTEX_H) + +#elif U_PLATFORM_USES_ONLY_WIN32_API + +/* For CRITICAL_SECTION */ + +/* + * Note: there is an earlier include of windows.h in this file, but it is in + * different conditionals. + * This one is needed if we are using C++11 for atomic ops, but + * win32 APIs for Critical Sections. + */ + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +# define VC_EXTRALEAN +# define NOUSER +# define NOSERVICE +# define NOIME +# define NOMCX +# ifndef NOMINMAX +# define NOMINMAX +# endif +# include + + +typedef struct UMutex { + icu::UInitOnce fInitOnce; + CRITICAL_SECTION fCS; +} UMutex; + +/* Initializer for a static UMUTEX. Deliberately contains no value for the + * CRITICAL_SECTION. */ +#define U_MUTEX_INITIALIZER {U_INITONCE_INITIALIZER} + +struct UConditionVar { + HANDLE fEntryGate; + HANDLE fExitGate; + int32_t fWaitCount; +}; + +#define U_CONDITION_INITIALIZER {NULL, NULL, 0} + + -/* Lock a mutex. +#elif U_PLATFORM_IMPLEMENTS_POSIX + +/* + * POSIX platform + */ + +#include + +struct UMutex { + pthread_mutex_t fMutex; +}; +typedef struct UMutex UMutex; +#define U_MUTEX_INITIALIZER {PTHREAD_MUTEX_INITIALIZER} + +struct UConditionVar { + pthread_cond_t fCondition; +}; +#define U_CONDITION_INITIALIZER {PTHREAD_COND_INITIALIZER} + +#else + +/* + * Unknow platform type. + * This is an error condition. ICU requires mutexes. + */ + +#error Unknown Platform. + +#endif + + + +/************************************************************************************** + * + * Mutex Implementation function declaratations. + * Declarations are platform neutral. + * Implementations, in umutex.cpp, are platform specific. + * + ************************************************************************************/ + +/* 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); /* - * Atomic Increment and Decrement of an int32_t value. + * Wait on a condition variable. + * The calling thread will unlock the mutex and wait on the condition variable. + * The mutex must be locked by the calling thread when invoking this function. * - * 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. + * @param cond the condition variable to wait on. + * @param mutex the associated mutex. */ -U_CAPI int32_t U_EXPORT2 umtx_atomic_inc(int32_t *); -U_CAPI int32_t U_EXPORT2 umtx_atomic_dec(int32_t *); +U_INTERNAL void U_EXPORT2 umtx_condWait(UConditionVar *cond, UMutex *mutex); -#endif /*_CMUTEX*/ -/*eof*/ +/* + * Broadcast wakeup of all threads waiting on a Condition. + * The associated mutex must be locked by the calling thread when calling + * this function; this is a temporary ICU restriction. + * + * @param cond the condition variable. + */ +U_INTERNAL void U_EXPORT2 umtx_condBroadcast(UConditionVar *cond); +/* + * Signal a condition variable, waking up one waiting thread. + * CAUTION: Do not use. Place holder only. Not implemented for Windows. + */ +U_INTERNAL void U_EXPORT2 umtx_condSignal(UConditionVar *cond); +#endif /* UMUTEX_H */ +/*eof*/