Coverage Report

Created: 2023-03-29 06:07

/src/icu/icu4c/source/common/umutex.h
Line
Count
Source (jump to first uncovered line)
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
61.9k
inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
76
61.9k
    return var.load(std::memory_order_acquire);
77
61.9k
}
78
79
9
inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
80
9
    var.store(val, std::memory_order_release);
81
9
}
82
83
0
inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
84
0
    return var->fetch_add(1) + 1;
85
0
}
86
87
0
inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
88
0
    return var->fetch_sub(1) - 1;
89
0
}
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
0
    void reset() {fState = 0;}
102
0
    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
557
inline void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)()) {
124
557
    if (umtx_loadAcquire(uio.fState) == 2) {
125
556
        return;
126
556
    }
127
1
    if (umtx_initImplPreInit(uio)) {
128
1
        (*fp)();
129
1
        umtx_initImplPostInit(uio);
130
1
    }
131
1
}
132
133
// umtx_initOnce variant for plain functions, or static class functions.
134
//               With ErrorCode, No context parameter.
135
61.2k
inline void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(UErrorCode &), UErrorCode &errCode) {
136
61.2k
    if (U_FAILURE(errCode)) {
137
1
        return;
138
1
    }
139
61.2k
    if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) {
140
        // We run the initialization.
141
3
        (*fp)(errCode);
142
3
        uio.fErrCode = errCode;
143
3
        umtx_initImplPostInit(uio);
144
61.2k
    } else {
145
        // Someone else already ran the initialization.
146
61.2k
        if (U_FAILURE(uio.fErrCode)) {
147
0
            errCode = uio.fErrCode;
148
0
        }
149
61.2k
    }
150
61.2k
}
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
0
    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
77.9k
    void lock() {
230
77.9k
        std::mutex *m = fMutex.load(std::memory_order_acquire);
231
77.9k
        if (m == nullptr) { m = getMutex(); }
232
77.9k
        m->lock();
233
77.9k
    }
234
77.9k
    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*/