michael@0: /* michael@0: ********************************************************************** michael@0: * Copyright (C) 1997-2013, International Business Machines michael@0: * Corporation and others. All Rights Reserved. michael@0: ********************************************************************** michael@0: * michael@0: * File UMUTEX.H michael@0: * michael@0: * Modification History: michael@0: * michael@0: * Date Name Description michael@0: * 04/02/97 aliu Creation. michael@0: * 04/07/99 srl rewrite - C interface, multiple mutices michael@0: * 05/13/99 stephen Changed to umutex (from cmutex) michael@0: ****************************************************************************** michael@0: */ michael@0: michael@0: #ifndef UMUTEX_H michael@0: #define UMUTEX_H michael@0: michael@0: #include "unicode/utypes.h" michael@0: #include "unicode/uclean.h" michael@0: #include "putilimp.h" michael@0: michael@0: michael@0: michael@0: // Forward Declarations. UMutex is not in the ICU namespace (yet) because michael@0: // there are some remaining references from plain C. michael@0: struct UMutex; michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: struct UInitOnce; michael@0: U_NAMESPACE_END michael@0: michael@0: // Stringify macros, to allow #include of user supplied atomic & mutex files. michael@0: #define U_MUTEX_STR(s) #s michael@0: #define U_MUTEX_XSTR(s) U_MUTEX_STR(s) michael@0: michael@0: /**************************************************************************** michael@0: * michael@0: * Low Level Atomic Operations. michael@0: * Compiler dependent. Not operating system dependent. michael@0: * michael@0: ****************************************************************************/ michael@0: #if defined (U_USER_ATOMICS_H) michael@0: #include U_MUTEX_XSTR(U_USER_ATOMICS_H) michael@0: michael@0: #elif U_HAVE_STD_ATOMICS michael@0: michael@0: // C++11 atomics are available. michael@0: michael@0: #include michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: typedef std::atomic u_atomic_int32_t; michael@0: #define ATOMIC_INT32_T_INITIALIZER(val) ATOMIC_VAR_INIT(val) michael@0: michael@0: inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { michael@0: return var.load(std::memory_order_acquire); michael@0: } michael@0: michael@0: inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { michael@0: var.store(val, std::memory_order_release); michael@0: } michael@0: michael@0: inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) { michael@0: return var->fetch_add(1) + 1; michael@0: } michael@0: michael@0: inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) { michael@0: return var->fetch_sub(1) - 1; michael@0: } michael@0: U_NAMESPACE_END michael@0: michael@0: #elif U_PLATFORM_HAS_WIN32_API michael@0: michael@0: // MSVC compiler. Reads and writes of volatile variables have michael@0: // acquire and release memory semantics, respectively. michael@0: // This is a Microsoft extension, not standard C++ behavior. michael@0: // michael@0: // Update: can't use this because of MinGW, built with gcc. michael@0: // Original plan was to use gcc atomics for MinGW, but they michael@0: // aren't supported, so we fold MinGW into this path. michael@0: michael@0: # define WIN32_LEAN_AND_MEAN michael@0: # define VC_EXTRALEAN michael@0: # define NOUSER michael@0: # define NOSERVICE michael@0: # define NOIME michael@0: # define NOMCX michael@0: # ifndef NOMINMAX michael@0: # define NOMINMAX michael@0: # endif michael@0: # include michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: typedef volatile LONG u_atomic_int32_t; michael@0: #define ATOMIC_INT32_T_INITIALIZER(val) val michael@0: michael@0: inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { michael@0: return InterlockedCompareExchange(&var, 0, 0); michael@0: } michael@0: michael@0: inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { michael@0: InterlockedExchange(&var, val); michael@0: } michael@0: michael@0: michael@0: inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) { michael@0: return InterlockedIncrement(var); michael@0: } michael@0: michael@0: inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) { michael@0: return InterlockedDecrement(var); michael@0: } michael@0: U_NAMESPACE_END michael@0: michael@0: michael@0: #elif U_HAVE_GCC_ATOMICS michael@0: /* michael@0: * gcc atomic ops. These are available on several other compilers as well. michael@0: */ michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: typedef int32_t u_atomic_int32_t; michael@0: #define ATOMIC_INT32_T_INITIALIZER(val) val michael@0: michael@0: inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { michael@0: int32_t val = var; michael@0: __sync_synchronize(); michael@0: return val; michael@0: } michael@0: michael@0: inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { michael@0: __sync_synchronize(); michael@0: var = val; michael@0: } michael@0: michael@0: inline int32_t umtx_atomic_inc(u_atomic_int32_t *p) { michael@0: return __sync_add_and_fetch(p, 1); michael@0: } michael@0: michael@0: inline int32_t umtx_atomic_dec(u_atomic_int32_t *p) { michael@0: return __sync_sub_and_fetch(p, 1); michael@0: } michael@0: U_NAMESPACE_END michael@0: michael@0: #else michael@0: michael@0: /* michael@0: * Unknown Platform. Use out-of-line functions, which in turn use mutexes. michael@0: * Slow but correct. michael@0: */ michael@0: michael@0: #define U_NO_PLATFORM_ATOMICS michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: typedef int32_t u_atomic_int32_t; michael@0: #define ATOMIC_INT32_T_INITIALIZER(val) val michael@0: michael@0: U_COMMON_API int32_t U_EXPORT2 michael@0: umtx_loadAcquire(u_atomic_int32_t &var); michael@0: michael@0: U_COMMON_API void U_EXPORT2 michael@0: umtx_storeRelease(u_atomic_int32_t &var, int32_t val); michael@0: michael@0: U_COMMON_API int32_t U_EXPORT2 michael@0: umtx_atomic_inc(u_atomic_int32_t *p); michael@0: michael@0: U_COMMON_API int32_t U_EXPORT2 michael@0: umtx_atomic_dec(u_atomic_int32_t *p); michael@0: michael@0: U_NAMESPACE_END michael@0: michael@0: #endif /* Low Level Atomic Ops Platfrom Chain */ michael@0: michael@0: michael@0: michael@0: /************************************************************************************************* michael@0: * michael@0: * UInitOnce Definitions. michael@0: * These are platform neutral. michael@0: * michael@0: *************************************************************************************************/ michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: struct UInitOnce { michael@0: u_atomic_int32_t fState; michael@0: UErrorCode fErrCode; michael@0: void reset() {fState = 0;}; michael@0: UBool isReset() {return umtx_loadAcquire(fState) == 0;}; michael@0: // Note: isReset() is used by service registration code. michael@0: // Thread safety of this usage needs review. michael@0: }; michael@0: michael@0: #define U_INITONCE_INITIALIZER {ATOMIC_INT32_T_INITIALIZER(0), U_ZERO_ERROR} michael@0: michael@0: michael@0: U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &); michael@0: U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &); michael@0: michael@0: template void umtx_initOnce(UInitOnce &uio, T *obj, void (T::*fp)()) { michael@0: if (umtx_loadAcquire(uio.fState) == 2) { michael@0: return; michael@0: } michael@0: if (umtx_initImplPreInit(uio)) { michael@0: (obj->*fp)(); michael@0: umtx_initImplPostInit(uio); michael@0: } michael@0: } michael@0: michael@0: michael@0: // umtx_initOnce variant for plain functions, or static class functions. michael@0: // No context parameter. michael@0: inline void umtx_initOnce(UInitOnce &uio, void (*fp)()) { michael@0: if (umtx_loadAcquire(uio.fState) == 2) { michael@0: return; michael@0: } michael@0: if (umtx_initImplPreInit(uio)) { michael@0: (*fp)(); michael@0: umtx_initImplPostInit(uio); michael@0: } michael@0: } michael@0: michael@0: // umtx_initOnce variant for plain functions, or static class functions. michael@0: // With ErrorCode, No context parameter. michael@0: inline void umtx_initOnce(UInitOnce &uio, void (*fp)(UErrorCode &), UErrorCode &errCode) { michael@0: if (U_FAILURE(errCode)) { michael@0: return; michael@0: } michael@0: if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) { michael@0: // We run the initialization. michael@0: (*fp)(errCode); michael@0: uio.fErrCode = errCode; michael@0: umtx_initImplPostInit(uio); michael@0: } else { michael@0: // Someone else already ran the initialization. michael@0: if (U_FAILURE(uio.fErrCode)) { michael@0: errCode = uio.fErrCode; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // umtx_initOnce variant for plain functions, or static class functions, michael@0: // with a context parameter. michael@0: template void umtx_initOnce(UInitOnce &uio, void (*fp)(T), T context) { michael@0: if (umtx_loadAcquire(uio.fState) == 2) { michael@0: return; michael@0: } michael@0: if (umtx_initImplPreInit(uio)) { michael@0: (*fp)(context); michael@0: umtx_initImplPostInit(uio); michael@0: } michael@0: } michael@0: michael@0: // umtx_initOnce variant for plain functions, or static class functions, michael@0: // with a context parameter and an error code. michael@0: template void umtx_initOnce(UInitOnce &uio, void (*fp)(T, UErrorCode &), T context, UErrorCode &errCode) { michael@0: if (U_FAILURE(errCode)) { michael@0: return; michael@0: } michael@0: if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) { michael@0: // We run the initialization. michael@0: (*fp)(context, errCode); michael@0: uio.fErrCode = errCode; michael@0: umtx_initImplPostInit(uio); michael@0: } else { michael@0: // Someone else already ran the initialization. michael@0: if (U_FAILURE(uio.fErrCode)) { michael@0: errCode = uio.fErrCode; michael@0: } michael@0: } michael@0: } michael@0: michael@0: U_NAMESPACE_END michael@0: michael@0: michael@0: michael@0: /************************************************************************************************* michael@0: * michael@0: * Mutex Definitions. Platform Dependent, #if platform chain follows. michael@0: * TODO: Add a C++11 version. michael@0: * Need to convert all mutex using files to C++ first. michael@0: * michael@0: *************************************************************************************************/ michael@0: michael@0: #if defined(U_USER_MUTEX_H) michael@0: // #inlcude "U_USER_MUTEX_H" michael@0: #include U_MUTEX_XSTR(U_USER_MUTEX_H) michael@0: michael@0: #elif U_PLATFORM_HAS_WIN32_API michael@0: michael@0: /* Windows Definitions. michael@0: * Windows comes first in the platform chain. michael@0: * Cygwin (and possibly others) have both WIN32 and POSIX APIs. Prefer Win32 in this case. michael@0: */ michael@0: michael@0: michael@0: /* For CRITICAL_SECTION */ michael@0: michael@0: /* michael@0: * Note: there is an earlier include of windows.h in this file, but it is in michael@0: * different conditionals. michael@0: * This one is needed if we are using C++11 for atomic ops, but michael@0: * win32 APIs for Critical Sections. michael@0: */ michael@0: michael@0: # define WIN32_LEAN_AND_MEAN michael@0: # define VC_EXTRALEAN michael@0: # define NOUSER michael@0: # define NOSERVICE michael@0: # define NOIME michael@0: # define NOMCX michael@0: # ifndef NOMINMAX michael@0: # define NOMINMAX michael@0: # endif michael@0: # include michael@0: michael@0: michael@0: typedef struct UMutex { michael@0: icu::UInitOnce fInitOnce; michael@0: CRITICAL_SECTION fCS; michael@0: } UMutex; michael@0: michael@0: /* Initializer for a static UMUTEX. Deliberately contains no value for the michael@0: * CRITICAL_SECTION. michael@0: */ michael@0: #define U_MUTEX_INITIALIZER {U_INITONCE_INITIALIZER} michael@0: michael@0: michael@0: michael@0: #elif U_PLATFORM_IMPLEMENTS_POSIX michael@0: michael@0: /* michael@0: * POSIX platform michael@0: */ michael@0: michael@0: #include michael@0: michael@0: struct UMutex { michael@0: pthread_mutex_t fMutex; michael@0: }; michael@0: typedef struct UMutex UMutex; michael@0: #define U_MUTEX_INITIALIZER {PTHREAD_MUTEX_INITIALIZER} michael@0: michael@0: #else michael@0: michael@0: /* michael@0: * Unknow platform type. michael@0: * This is an error condition. ICU requires mutexes. michael@0: */ michael@0: michael@0: #error Unknown Platform. michael@0: michael@0: #endif michael@0: michael@0: michael@0: michael@0: /************************************************************************************** michael@0: * michael@0: * Mutex Implementation function declaratations. michael@0: * Declarations are platform neutral. michael@0: * Implementations, in umutex.cpp, are platform specific. michael@0: * michael@0: ************************************************************************************/ michael@0: michael@0: /* Lock a mutex. michael@0: * @param mutex The given mutex to be locked. Pass NULL to specify michael@0: * the global ICU mutex. Recursive locks are an error michael@0: * and may cause a deadlock on some platforms. michael@0: */ michael@0: U_INTERNAL void U_EXPORT2 umtx_lock(UMutex* mutex); michael@0: michael@0: /* Unlock a mutex. michael@0: * @param mutex The given mutex to be unlocked. Pass NULL to specify michael@0: * the global ICU mutex. michael@0: */ michael@0: U_INTERNAL void U_EXPORT2 umtx_unlock (UMutex* mutex); michael@0: michael@0: #endif /* UMUTEX_H */ michael@0: /*eof*/