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