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