umutex.h 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. // © 2016 and later: Unicode, Inc. and others.
  2. // License & terms of use: http://www.unicode.org/copyright.html
  3. /*
  4. **********************************************************************
  5. * Copyright (C) 1997-2015, International Business Machines
  6. * Corporation and others. All Rights Reserved.
  7. **********************************************************************
  8. *
  9. * File UMUTEX.H
  10. *
  11. * Modification History:
  12. *
  13. * Date Name Description
  14. * 04/02/97 aliu Creation.
  15. * 04/07/99 srl rewrite - C interface, multiple mutices
  16. * 05/13/99 stephen Changed to umutex (from cmutex)
  17. ******************************************************************************
  18. */
  19. #ifndef UMUTEX_H
  20. #define UMUTEX_H
  21. #include <atomic>
  22. #include <condition_variable>
  23. #include <mutex>
  24. #include <type_traits>
  25. #include "unicode/utypes.h"
  26. #include "unicode/uclean.h"
  27. #include "unicode/uobject.h"
  28. #include "putilimp.h"
  29. #if defined(U_USER_ATOMICS_H) || defined(U_USER_MUTEX_H)
  30. // Support for including an alternate implementation of atomic & mutex operations has been withdrawn.
  31. // See issue ICU-20185.
  32. #error U_USER_ATOMICS and U_USER_MUTEX_H are not supported
  33. #endif
  34. // Export an explicit template instantiation of std::atomic<int32_t>.
  35. // When building DLLs for Windows this is required as it is used as a data member of the exported SharedObject class.
  36. // See digitlst.h, pluralaffix.h, datefmt.h, and others for similar examples.
  37. //
  38. // Similar story for std::atomic<std::mutex *>, and the exported UMutex class.
  39. #if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN && !defined(U_IN_DOXYGEN)
  40. #if defined(__clang__) || defined(_MSC_VER)
  41. #if defined(__clang__)
  42. // Suppress the warning that the explicit instantiation after explicit specialization has no effect.
  43. #pragma clang diagnostic push
  44. #pragma clang diagnostic ignored "-Winstantiation-after-specialization"
  45. #endif
  46. template struct U_COMMON_API std::atomic<int32_t>;
  47. template struct U_COMMON_API std::atomic<std::mutex *>;
  48. #if defined(__clang__)
  49. #pragma clang diagnostic pop
  50. #endif
  51. #elif defined(__GNUC__)
  52. // For GCC this class is already exported/visible, so no need for U_COMMON_API.
  53. template struct std::atomic<int32_t>;
  54. template struct std::atomic<std::mutex *>;
  55. #endif
  56. #endif
  57. U_NAMESPACE_BEGIN
  58. /****************************************************************************
  59. *
  60. * Low Level Atomic Operations, ICU wrappers for.
  61. *
  62. ****************************************************************************/
  63. typedef std::atomic<int32_t> u_atomic_int32_t;
  64. #define ATOMIC_INT32_T_INITIALIZER(val) ATOMIC_VAR_INIT(val)
  65. inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
  66. return var.load(std::memory_order_acquire);
  67. }
  68. inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
  69. var.store(val, std::memory_order_release);
  70. }
  71. inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
  72. return var->fetch_add(1) + 1;
  73. }
  74. inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
  75. return var->fetch_sub(1) - 1;
  76. }
  77. /*************************************************************************************************
  78. *
  79. * UInitOnce Definitions.
  80. *
  81. *************************************************************************************************/
  82. struct UInitOnce {
  83. u_atomic_int32_t fState;
  84. UErrorCode fErrCode;
  85. void reset() {fState = 0;}
  86. UBool isReset() {return umtx_loadAcquire(fState) == 0;}
  87. // Note: isReset() is used by service registration code.
  88. // Thread safety of this usage needs review.
  89. };
  90. #define U_INITONCE_INITIALIZER {ATOMIC_INT32_T_INITIALIZER(0), U_ZERO_ERROR}
  91. U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &);
  92. U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &);
  93. template<class T> void umtx_initOnce(UInitOnce &uio, T *obj, void (U_CALLCONV T::*fp)()) {
  94. if (umtx_loadAcquire(uio.fState) == 2) {
  95. return;
  96. }
  97. if (umtx_initImplPreInit(uio)) {
  98. (obj->*fp)();
  99. umtx_initImplPostInit(uio);
  100. }
  101. }
  102. // umtx_initOnce variant for plain functions, or static class functions.
  103. // No context parameter.
  104. inline void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)()) {
  105. if (umtx_loadAcquire(uio.fState) == 2) {
  106. return;
  107. }
  108. if (umtx_initImplPreInit(uio)) {
  109. (*fp)();
  110. umtx_initImplPostInit(uio);
  111. }
  112. }
  113. // umtx_initOnce variant for plain functions, or static class functions.
  114. // With ErrorCode, No context parameter.
  115. inline void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(UErrorCode &), UErrorCode &errCode) {
  116. if (U_FAILURE(errCode)) {
  117. return;
  118. }
  119. if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) {
  120. // We run the initialization.
  121. (*fp)(errCode);
  122. uio.fErrCode = errCode;
  123. umtx_initImplPostInit(uio);
  124. } else {
  125. // Someone else already ran the initialization.
  126. if (U_FAILURE(uio.fErrCode)) {
  127. errCode = uio.fErrCode;
  128. }
  129. }
  130. }
  131. // umtx_initOnce variant for plain functions, or static class functions,
  132. // with a context parameter.
  133. template<class T> void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(T), T context) {
  134. if (umtx_loadAcquire(uio.fState) == 2) {
  135. return;
  136. }
  137. if (umtx_initImplPreInit(uio)) {
  138. (*fp)(context);
  139. umtx_initImplPostInit(uio);
  140. }
  141. }
  142. // umtx_initOnce variant for plain functions, or static class functions,
  143. // with a context parameter and an error code.
  144. template<class T> void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(T, UErrorCode &), T context, UErrorCode &errCode) {
  145. if (U_FAILURE(errCode)) {
  146. return;
  147. }
  148. if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) {
  149. // We run the initialization.
  150. (*fp)(context, errCode);
  151. uio.fErrCode = errCode;
  152. umtx_initImplPostInit(uio);
  153. } else {
  154. // Someone else already ran the initialization.
  155. if (U_FAILURE(uio.fErrCode)) {
  156. errCode = uio.fErrCode;
  157. }
  158. }
  159. }
  160. // UMutex should be constexpr-constructible, so that no initialization code
  161. // is run during startup.
  162. // This works on all C++ libraries except MS VS before VS2019.
  163. #if (defined(_CPPLIB_VER) && !defined(_MSVC_STL_VERSION)) || \
  164. (defined(_MSVC_STL_VERSION) && _MSVC_STL_VERSION < 142)
  165. // (VS std lib older than VS2017) || (VS std lib version < VS2019)
  166. # define UMUTEX_CONSTEXPR
  167. #else
  168. # define UMUTEX_CONSTEXPR constexpr
  169. #endif
  170. /**
  171. * UMutex - ICU Mutex class.
  172. *
  173. * This is the preferred Mutex class for use within ICU implementation code.
  174. * It is a thin wrapper over C++ std::mutex, with these additions:
  175. * - Static instances are safe, not triggering static construction or destruction,
  176. * and the associated order of construction or destruction issues.
  177. * - Plumbed into u_cleanup() for destructing the underlying std::mutex,
  178. * which frees any OS level resources they may be holding.
  179. *
  180. * Limitations:
  181. * - Static or global instances only. Cannot be heap allocated. Cannot appear as a
  182. * member of another class.
  183. * - No condition variables or other advanced features. If needed, you will need to use
  184. * std::mutex and std::condition_variable directly. For an example, see unifiedcache.cpp
  185. *
  186. * Typical Usage:
  187. * static UMutex myMutex;
  188. *
  189. * {
  190. * Mutex lock(myMutex);
  191. * ... // Do stuff that is protected by myMutex;
  192. * } // myMutex is released when lock goes out of scope.
  193. */
  194. class U_COMMON_API UMutex {
  195. public:
  196. UMUTEX_CONSTEXPR UMutex() {}
  197. ~UMutex() = default;
  198. UMutex(const UMutex &other) = delete;
  199. UMutex &operator =(const UMutex &other) = delete;
  200. void *operator new(size_t) = delete;
  201. // requirements for C++ BasicLockable, allows UMutex to work with std::lock_guard
  202. void lock() {
  203. std::mutex *m = fMutex.load(std::memory_order_acquire);
  204. if (m == nullptr) { m = getMutex(); }
  205. m->lock();
  206. }
  207. void unlock() { fMutex.load(std::memory_order_relaxed)->unlock(); }
  208. static void cleanup();
  209. private:
  210. alignas(std::mutex) char fStorage[sizeof(std::mutex)] {};
  211. std::atomic<std::mutex *> fMutex { nullptr };
  212. /** All initialized UMutexes are kept in a linked list, so that they can be found,
  213. * and the underlying std::mutex destructed, by u_cleanup().
  214. */
  215. UMutex *fListLink { nullptr };
  216. static UMutex *gListHead;
  217. /** Out-of-line function to lazily initialize a UMutex on first use.
  218. * Initial fast check is inline, in lock(). The returned value may never
  219. * be nullptr.
  220. */
  221. std::mutex *getMutex();
  222. };
  223. /* Lock a mutex.
  224. * @param mutex The given mutex to be locked. Pass NULL to specify
  225. * the global ICU mutex. Recursive locks are an error
  226. * and may cause a deadlock on some platforms.
  227. */
  228. U_INTERNAL void U_EXPORT2 umtx_lock(UMutex* mutex);
  229. /* Unlock a mutex.
  230. * @param mutex The given mutex to be unlocked. Pass NULL to specify
  231. * the global ICU mutex.
  232. */
  233. U_INTERNAL void U_EXPORT2 umtx_unlock (UMutex* mutex);
  234. U_NAMESPACE_END
  235. #endif /* UMUTEX_H */
  236. /*eof*/