+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
/*
**********************************************************************
-* Copyright (C) 1997-2008, International Business Machines
+* Copyright (C) 1997-2015, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
*
#ifndef UMUTEX_H
#define UMUTEX_H
+#include <atomic>
+#include <condition_variable>
+#include <mutex>
+
#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<int32_t>.
+// 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<int32_t>;
+ #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<int32_t>;
+#endif
#endif
-/**
- * \def UMTX_CHECK
- * Encapsulates a safe check of an expression
- * for use with double-checked lazy inititialization.
- * On CPUs with weak memory models, this must use memory fence instructions
- * or mutexes.
- * 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.
+
+U_NAMESPACE_BEGIN
+
+/****************************************************************************
*
- *
- * @internal
- */
-#if UMTX_STRONG_MEMORY_MODEL
+ * Low Level Atomic Operations, ICU wrappers for.
+ *
+ ****************************************************************************/
-#define UMTX_CHECK(pMutex, expression, result) \
- (result)=(expression)
+typedef std::atomic<int32_t> u_atomic_int32_t;
+#define ATOMIC_INT32_T_INITIALIZER(val) ATOMIC_VAR_INIT(val)
-#else
+inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
+ return var.load(std::memory_order_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) {
+ var.store(val, std::memory_order_release);
+}
-#endif
+inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
+ return var->fetch_add(1) + 1;
+}
-/*
- * 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 int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
+ return var->fetch_sub(1) - 1;
+}
+
+
+/*************************************************************************************************
+ *
+ * UInitOnce Definitions.
*
- * For example:
+ *************************************************************************************************/
+
+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 (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<class T> 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<class T> 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.
*
- * 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);
- * }
+ * 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*/
-
-
-