X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/b75a7d8f3b4adbae880cab104ce2c6a50eee4db2..b331163bffd790ced0e88b73f44f86d49ccc48a5:/icuSources/common/umutex.h diff --git a/icuSources/common/umutex.h b/icuSources/common/umutex.h index ae713bed..e0ad0d3c 100644 --- a/icuSources/common/umutex.h +++ b/icuSources/common/umutex.h @@ -1,6 +1,6 @@ /* ********************************************************************** -* Copyright (C) 1997-2001, International Business Machines +* Copyright (C) 1997-2014, International Business Machines * Corporation and others. All Rights Reserved. ********************************************************************** * @@ -19,96 +19,406 @@ #define UMUTEX_H #include "unicode/utypes.h" +#include "unicode/uclean.h" +#include "putilimp.h" -/** - * Mutex data type. - * @internal + + +// 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) + +/**************************************************************************** + * + * 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. + +# define WIN32_LEAN_AND_MEAN +# 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); +} + + +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_GCC_ATOMICS +/* + * gcc atomic ops. These are available on several other compilers as well. */ -typedef void *UMTX; -/* APP_NO_THREADS is an old symbol. We'll honour it if present. */ -#ifdef APP_NO_THREADS -# define ICU_USE_THREADS 0 -#endif +U_NAMESPACE_BEGIN +typedef int32_t u_atomic_int32_t; +#define ATOMIC_INT32_T_INITIALIZER(val) val -/* Default: use threads. */ -#ifndef ICU_USE_THREADS -# define ICU_USE_THREADS 1 -#endif +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 this library which accesses protected data should - * instantiate a Mutex object while doing so. Notice that there is - * only one coarse-grained lock which applies to this entire library, - * 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 */ + + + +/************************************************************************************************* + * + * UInitOnce Definitions. + * These are platform neutral. * - * For example: + *************************************************************************************************/ + +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 (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 (*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 (*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 (*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 (*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 + + + +/************************************************************************************************* * - * void Function(int arg1, int arg2) - * { - * static Object* foo; // Shared read-write object - * Mutex mutex; - * foo->Method(); - * // When 'mutex' goes out of scope and gets destroyed here - * // the lock is released - * } + * Mutex Definitions. Platform Dependent, #if platform chain follows. + * TODO: Add a C++11 version. + * Need to convert all mutex using files to C++ first. * - * Note: Do NOT use the form 'Mutex mutex();' as that merely - * forward-declares a function returning a Mutex. This is a common - * mistake which silently slips through the compiler!! */ + *************************************************************************************************/ + +#if defined(U_USER_MUTEX_H) +// #inlcude "U_USER_MUTEX_H" +#include U_MUTEX_XSTR(U_USER_MUTEX_H) +#elif U_PLATFORM_HAS_WIN32_API -/* Lock a mutex. Pass in NULL if you want the (ick) Single Global - Mutex. - * @param mutex The given mutex to be locked +/* Windows Definitions. + * Windows comes first in the platform chain. + * Cygwin (and possibly others) have both WIN32 and POSIX APIs. Prefer Win32 in this case. */ -U_CAPI void U_EXPORT2 umtx_lock ( UMTX* mutex ); -/* Unlock a mutex. Pass in NULL if you want the single global - mutex. - * @param mutex The given mutex to be unlocked + +/* 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. */ -U_CAPI void U_EXPORT2 umtx_unlock ( UMTX* mutex ); -/* Initialize a mutex. Use it this way: - umtx_init( &aMutex ); - * ICU Mutexes, aside from the global mutex, must be explicitly initialized - * before use. - * @param mutex The given mutex to be initialized +# define WIN32_LEAN_AND_MEAN +# 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. */ -U_CAPI void U_EXPORT2 umtx_init ( UMTX* mutex ); +#define U_MUTEX_INITIALIZER {U_INITONCE_INITIALIZER} -/* Destroy a mutex. This will free the resources of a mutex. - Use it this way: - umtx_destroy( &aMutex ); - * @param mutex The given mutex to be destroyed +struct UConditionVar { + HANDLE fEntryGate; + HANDLE fExitGate; + int32_t fWaitCount; +}; + +#define U_CONDITION_INITIALIZER {NULL, NULL, 0} + + + +#elif U_PLATFORM_IMPLEMENTS_POSIX + +/* + * POSIX platform */ -U_CAPI void U_EXPORT2 umtx_destroy( UMTX *mutex ); - -/* Is a mutex initialized? - Use it this way: - umtx_isInitialized( &aMutex ); - This function is not normally needed. It is more efficient to - unconditionally call umtx_init(&aMutex) than it is to check first. - * @param mutex The given mutex to be tested -*/ -U_CAPI UBool U_EXPORT2 umtx_isInitialized( UMTX *mutex ); + +#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 /* - * Atomic Increment and Decrement of an int32_t value. + * 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. * - * 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. + ************************************************************************************/ + +/* 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 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_lock(UMutex* mutex); +/* Unlock a mutex. + * @param mutex The given mutex to be unlocked. Pass NULL to specify + * the global ICU mutex. + */ +U_INTERNAL void U_EXPORT2 umtx_unlock (UMutex* mutex); -#endif /*_CMUTEX*/ -/*eof*/ +/* + * 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. + * + * @param cond the condition variable to wait on. + * @param mutex the associated mutex. + */ + +U_INTERNAL void U_EXPORT2 umtx_condWait(UConditionVar *cond, UMutex *mutex); +/* + * 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*/