/*
**********************************************************************
-* Copyright (C) 1997-2012, International Business Machines
+* Copyright (C) 1997-2013, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
*
#include "unicode/uclean.h"
#include "putilimp.h"
-/* For _ReadWriteBarrier(). */
-#if defined(_MSC_VER) && _MSC_VER >= 1500
-# include <intrin.h>
-#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.
- */
+
+// Forward Declarations. UMutex is not in the ICU namespace (yet) because
+// there are some remaining references from plain C.
+struct UMutex;
+
+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 <atomic>
+
+U_NAMESPACE_BEGIN
+
+typedef std::atomic<int32_t> 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 <windows.h>
-#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 <libkern/OSAtomic.h>
-#define USE_MAC_OS_ATOMIC_INCREMENT 1
-#if defined(UPRV_REMAP_INLINE)
-#undef inline
-#undef UPRV_REMAP_INLINE
-#endif
-#endif
+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
/*
- * 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
+ * gcc atomic ops. These are available on several other compilers as well.
*/
-#ifndef ANNOTATE_HAPPENS_BEFORE
-# define ANNOTATE_HAPPENS_BEFORE(obj)
-# define ANNOTATE_HAPPENS_AFTER(obj)
-# define ANNOTATE_UNPROTECTED_READ(x) (x)
-#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
+U_NAMESPACE_BEGIN
+typedef int32_t u_atomic_int32_t;
+#define ATOMIC_INT32_T_INITIALIZER(val) val
-#ifndef UMTX_ACQUIRE_BARRIER
-# define UMTX_ACQUIRE_BARRIER UMTX_FULL_BARRIER
-#endif
+inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
+ int32_t val = var;
+ __sync_synchronize();
+ return val;
+}
-#ifndef UMTX_RELEASE_BARRIER
-# define UMTX_RELEASE_BARRIER UMTX_FULL_BARRIER
-#endif
+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
-/**
- * \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.
+ * Unknown Platform. Use out-of-line functions, which in turn use mutexes.
+ * Slow but correct.
*/
-/*
- * 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.
+#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<class T> 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<class T> 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<class T> 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
+
+
+
+/*************************************************************************************************
*
- * an alternative C++ mutex API is defined in the file common/mutex.h
- */
+ * Mutex Definitions. Platform Dependent, #if platform chain follows.
+ * TODO: Add a C++11 version.
+ * Need to convert all mutex using files to C++ first.
+ *
+ *************************************************************************************************/
-/*
- * 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.
+#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
+
+/* Windows Definitions.
+ * Windows comes first in the platform chain.
+ * Cygwin (and possibly others) have both WIN32 and POSIX APIs. Prefer Win32 in this case.
*/
-#if U_PLATFORM_HAS_WIN32_API
+/* For CRITICAL_SECTION */
-/* 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.
+/*
+ * 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.
*/
-typedef struct U_INIT_ONCE {
- long fState;
- void *fContext;
-} U_INIT_ONCE;
-#define U_INIT_ONCE_STATIC_INIT {0, NULL}
+
+# define WIN32_LEAN_AND_MEAN
+# define VC_EXTRALEAN
+# define NOUSER
+# define NOSERVICE
+# define NOIME
+# define NOMCX
+# ifndef NOMINMAX
+# define NOMINMAX
+# endif
+# include <windows.h>
+
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];
+ 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_INIT_ONCE_STATIC_INIT, NULL, FALSE}
+#define U_MUTEX_INITIALIZER {U_INITONCE_INITIALIZER}
+
+
#elif U_PLATFORM_IMPLEMENTS_POSIX
+
+/*
+ * POSIX platform
+ */
+
#include <pthread.h>
struct UMutex {
pthread_mutex_t fMutex;
- UMTX fUserMutex;
- UBool fInitialized;
};
-#define U_MUTEX_INITIALIZER {PTHREAD_MUTEX_INITIALIZER, NULL, FALSE}
+typedef struct UMutex UMutex;
+#define U_MUTEX_INITIALIZER {PTHREAD_MUTEX_INITIALIZER}
#else
-/* Unknow platform type. */
-struct UMutex {
- void *fMutex;
-};
-#define U_MUTEX_INITIALIZER {NULL}
+
+/*
+ * Unknow platform type.
+ * This is an error condition. ICU requires mutexes.
+ */
+
#error Unknown Platform.
#endif
-#if (U_PLATFORM != U_PF_CYGWIN && U_PLATFORM != U_PF_MINGW) || defined(CYGWINMSVC)
-typedef struct UMutex UMutex;
-#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(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);
-
-/*
- * 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 *);
+U_INTERNAL void U_EXPORT2 umtx_unlock (UMutex* mutex);
-#endif /*_CMUTEX*/
+#endif /* UMUTEX_H */
/*eof*/