umutex.h 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  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. inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
  65. return var.load(std::memory_order_acquire);
  66. }
  67. inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
  68. var.store(val, std::memory_order_release);
  69. }
  70. inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
  71. return var->fetch_add(1) + 1;
  72. }
  73. inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
  74. return var->fetch_sub(1) - 1;
  75. }
  76. /*************************************************************************************************
  77. *
  78. * UInitOnce Definitions.
  79. *
  80. *************************************************************************************************/
  81. struct U_COMMON_API UInitOnce {
  82. u_atomic_int32_t fState {0};
  83. UErrorCode fErrCode {U_ZERO_ERROR};
  84. void reset() {fState = 0;}
  85. UBool isReset() {return umtx_loadAcquire(fState) == 0;}
  86. // Note: isReset() is used by service registration code.
  87. // Thread safety of this usage needs review.
  88. };
  89. U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &);
  90. U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &);
  91. template<class T> void umtx_initOnce(UInitOnce &uio, T *obj, void (U_CALLCONV T::*fp)()) {
  92. if (umtx_loadAcquire(uio.fState) == 2) {
  93. return;
  94. }
  95. if (umtx_initImplPreInit(uio)) {
  96. (obj->*fp)();
  97. umtx_initImplPostInit(uio);
  98. }
  99. }
  100. // umtx_initOnce variant for plain functions, or static class functions.
  101. // No context parameter.
  102. inline void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)()) {
  103. if (umtx_loadAcquire(uio.fState) == 2) {
  104. return;
  105. }
  106. if (umtx_initImplPreInit(uio)) {
  107. (*fp)();
  108. umtx_initImplPostInit(uio);
  109. }
  110. }
  111. // umtx_initOnce variant for plain functions, or static class functions.
  112. // With ErrorCode, No context parameter.
  113. inline void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(UErrorCode &), UErrorCode &errCode) {
  114. if (U_FAILURE(errCode)) {
  115. return;
  116. }
  117. if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) {
  118. // We run the initialization.
  119. (*fp)(errCode);
  120. uio.fErrCode = errCode;
  121. umtx_initImplPostInit(uio);
  122. } else {
  123. // Someone else already ran the initialization.
  124. if (U_FAILURE(uio.fErrCode)) {
  125. errCode = uio.fErrCode;
  126. }
  127. }
  128. }
  129. // umtx_initOnce variant for plain functions, or static class functions,
  130. // with a context parameter.
  131. template<class T> void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(T), T context) {
  132. if (umtx_loadAcquire(uio.fState) == 2) {
  133. return;
  134. }
  135. if (umtx_initImplPreInit(uio)) {
  136. (*fp)(context);
  137. umtx_initImplPostInit(uio);
  138. }
  139. }
  140. // umtx_initOnce variant for plain functions, or static class functions,
  141. // with a context parameter and an error code.
  142. template<class T> void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(T, UErrorCode &), T context, UErrorCode &errCode) {
  143. if (U_FAILURE(errCode)) {
  144. return;
  145. }
  146. if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) {
  147. // We run the initialization.
  148. (*fp)(context, errCode);
  149. uio.fErrCode = errCode;
  150. umtx_initImplPostInit(uio);
  151. } else {
  152. // Someone else already ran the initialization.
  153. if (U_FAILURE(uio.fErrCode)) {
  154. errCode = uio.fErrCode;
  155. }
  156. }
  157. }
  158. // UMutex should be constexpr-constructible, so that no initialization code
  159. // is run during startup.
  160. // This works on all C++ libraries except MS VS before VS2019.
  161. #if (defined(_CPPLIB_VER) && !defined(_MSVC_STL_VERSION)) || \
  162. (defined(_MSVC_STL_VERSION) && _MSVC_STL_VERSION < 142)
  163. // (VS std lib older than VS2017) || (VS std lib version < VS2019)
  164. # define UMUTEX_CONSTEXPR
  165. #else
  166. # define UMUTEX_CONSTEXPR constexpr
  167. #endif
  168. /**
  169. * UMutex - ICU Mutex class.
  170. *
  171. * This is the preferred Mutex class for use within ICU implementation code.
  172. * It is a thin wrapper over C++ std::mutex, with these additions:
  173. * - Static instances are safe, not triggering static construction or destruction,
  174. * and the associated order of construction or destruction issues.
  175. * - Plumbed into u_cleanup() for destructing the underlying std::mutex,
  176. * which frees any OS level resources they may be holding.
  177. *
  178. * Limitations:
  179. * - Static or global instances only. Cannot be heap allocated. Cannot appear as a
  180. * member of another class.
  181. * - No condition variables or other advanced features. If needed, you will need to use
  182. * std::mutex and std::condition_variable directly. For an example, see unifiedcache.cpp
  183. *
  184. * Typical Usage:
  185. * static UMutex myMutex;
  186. *
  187. * {
  188. * Mutex lock(myMutex);
  189. * ... // Do stuff that is protected by myMutex;
  190. * } // myMutex is released when lock goes out of scope.
  191. */
  192. class U_COMMON_API UMutex {
  193. public:
  194. UMUTEX_CONSTEXPR UMutex() {}
  195. ~UMutex() = default;
  196. UMutex(const UMutex &other) = delete;
  197. UMutex &operator =(const UMutex &other) = delete;
  198. void *operator new(size_t) = delete;
  199. // requirements for C++ BasicLockable, allows UMutex to work with std::lock_guard
  200. void lock() {
  201. std::mutex *m = fMutex.load(std::memory_order_acquire);
  202. if (m == nullptr) { m = getMutex(); }
  203. m->lock();
  204. }
  205. void unlock() { fMutex.load(std::memory_order_relaxed)->unlock(); }
  206. static void cleanup();
  207. private:
  208. alignas(std::mutex) char fStorage[sizeof(std::mutex)] {};
  209. std::atomic<std::mutex *> fMutex { nullptr };
  210. /** All initialized UMutexes are kept in a linked list, so that they can be found,
  211. * and the underlying std::mutex destructed, by u_cleanup().
  212. */
  213. UMutex *fListLink { nullptr };
  214. static UMutex *gListHead;
  215. /** Out-of-line function to lazily initialize a UMutex on first use.
  216. * Initial fast check is inline, in lock(). The returned value may never
  217. * be nullptr.
  218. */
  219. std::mutex *getMutex();
  220. };
  221. /* Lock a mutex.
  222. * @param mutex The given mutex to be locked. Pass NULL to specify
  223. * the global ICU mutex. Recursive locks are an error
  224. * and may cause a deadlock on some platforms.
  225. */
  226. U_CAPI void U_EXPORT2 umtx_lock(UMutex* mutex);
  227. /* Unlock a mutex.
  228. * @param mutex The given mutex to be unlocked. Pass NULL to specify
  229. * the global ICU mutex.
  230. */
  231. U_CAPI void U_EXPORT2 umtx_unlock (UMutex* mutex);
  232. U_NAMESPACE_END
  233. #endif /* UMUTEX_H */
  234. /*eof*/