/src/icu/source/common/umutex.cpp
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 | | * |
6 | | * Copyright (C) 1997-2016, International Business Machines |
7 | | * Corporation and others. All Rights Reserved. |
8 | | * |
9 | | ****************************************************************************** |
10 | | * |
11 | | * File umutex.cpp |
12 | | * |
13 | | * Modification History: |
14 | | * |
15 | | * Date Name Description |
16 | | * 04/02/97 aliu Creation. |
17 | | * 04/07/99 srl updated |
18 | | * 05/13/99 stephen Changed to umutex (from cmutex). |
19 | | * 11/22/99 aliu Make non-global mutex autoinitialize [j151] |
20 | | ****************************************************************************** |
21 | | */ |
22 | | |
23 | | #include "umutex.h" |
24 | | |
25 | | #include "unicode/utypes.h" |
26 | | #include "uassert.h" |
27 | | #include "ucln_cmn.h" |
28 | | #include "cmemory.h" |
29 | | |
30 | | U_NAMESPACE_BEGIN |
31 | | |
32 | | |
33 | | #if defined(U_USER_MUTEX_CPP) |
34 | | // Support for including an alternate implementation of mutexes has been withdrawn. |
35 | | // See issue ICU-20185. |
36 | | #error U_USER_MUTEX_CPP not supported |
37 | | #endif |
38 | | |
39 | | |
40 | | /************************************************************************************************* |
41 | | * |
42 | | * ICU Mutex wrappers. |
43 | | * |
44 | | *************************************************************************************************/ |
45 | | |
46 | | namespace { |
47 | | std::mutex *initMutex; |
48 | | std::condition_variable *initCondition; |
49 | | |
50 | | // The ICU global mutex. |
51 | | // Used when ICU implementation code passes nullptr for the mutex pointer. |
52 | | UMutex globalMutex; |
53 | | |
54 | | std::once_flag initFlag; |
55 | | std::once_flag *pInitFlag = &initFlag; |
56 | | |
57 | | } // Anonymous namespace |
58 | | |
59 | | U_CDECL_BEGIN |
60 | 0 | static UBool U_CALLCONV umtx_cleanup() { |
61 | 0 | initMutex->~mutex(); |
62 | 0 | initCondition->~condition_variable(); |
63 | 0 | UMutex::cleanup(); |
64 | | |
65 | | // Reset the once_flag, by destructing it and creating a fresh one in its place. |
66 | | // Do not use this trick anywhere else in ICU; use umtx_initOnce, not std::call_once(). |
67 | 0 | pInitFlag->~once_flag(); |
68 | 0 | pInitFlag = new(&initFlag) std::once_flag(); |
69 | 0 | return true; |
70 | 0 | } |
71 | | |
72 | 0 | static void U_CALLCONV umtx_init() { |
73 | 0 | initMutex = STATIC_NEW(std::mutex); |
74 | 0 | initCondition = STATIC_NEW(std::condition_variable); |
75 | 0 | ucln_common_registerCleanup(UCLN_COMMON_MUTEX, umtx_cleanup); |
76 | 0 | } |
77 | | U_CDECL_END |
78 | | |
79 | | |
80 | 0 | std::mutex *UMutex::getMutex() { |
81 | 0 | std::mutex *retPtr = fMutex.load(std::memory_order_acquire); |
82 | 0 | if (retPtr == nullptr) { |
83 | 0 | std::call_once(*pInitFlag, umtx_init); |
84 | 0 | std::lock_guard<std::mutex> guard(*initMutex); |
85 | 0 | retPtr = fMutex.load(std::memory_order_acquire); |
86 | 0 | if (retPtr == nullptr) { |
87 | 0 | fMutex = new(fStorage) std::mutex(); |
88 | 0 | retPtr = fMutex; |
89 | 0 | fListLink = gListHead; |
90 | 0 | gListHead = this; |
91 | 0 | } |
92 | 0 | } |
93 | 0 | U_ASSERT(retPtr != nullptr); |
94 | 0 | return retPtr; |
95 | 0 | } |
96 | | |
97 | | UMutex *UMutex::gListHead = nullptr; |
98 | | |
99 | 0 | void UMutex::cleanup() { |
100 | 0 | UMutex *next = nullptr; |
101 | 0 | for (UMutex *m = gListHead; m != nullptr; m = next) { |
102 | 0 | (*m->fMutex).~mutex(); |
103 | 0 | m->fMutex = nullptr; |
104 | 0 | next = m->fListLink; |
105 | 0 | m->fListLink = nullptr; |
106 | 0 | } |
107 | 0 | gListHead = nullptr; |
108 | 0 | } |
109 | | |
110 | | |
111 | | U_CAPI void U_EXPORT2 |
112 | 0 | umtx_lock(UMutex *mutex) { |
113 | 0 | if (mutex == nullptr) { |
114 | 0 | mutex = &globalMutex; |
115 | 0 | } |
116 | 0 | mutex->lock(); |
117 | 0 | } |
118 | | |
119 | | |
120 | | U_CAPI void U_EXPORT2 |
121 | | umtx_unlock(UMutex* mutex) |
122 | 0 | { |
123 | 0 | if (mutex == nullptr) { |
124 | 0 | mutex = &globalMutex; |
125 | 0 | } |
126 | 0 | mutex->unlock(); |
127 | 0 | } |
128 | | |
129 | | |
130 | | /************************************************************************************************* |
131 | | * |
132 | | * UInitOnce Implementation |
133 | | * |
134 | | *************************************************************************************************/ |
135 | | |
136 | | // This function is called when a test of a UInitOnce::fState reveals that |
137 | | // initialization has not completed, that we either need to call the init |
138 | | // function on this thread, or wait for some other thread to complete. |
139 | | // |
140 | | // The actual call to the init function is made inline by template code |
141 | | // that knows the C++ types involved. This function returns true if |
142 | | // the caller needs to call the Init function. |
143 | | // |
144 | | U_COMMON_API UBool U_EXPORT2 |
145 | 0 | umtx_initImplPreInit(UInitOnce &uio) { |
146 | 0 | std::call_once(*pInitFlag, umtx_init); |
147 | 0 | std::unique_lock<std::mutex> lock(*initMutex); |
148 | 0 | if (umtx_loadAcquire(uio.fState) == 0) { |
149 | 0 | umtx_storeRelease(uio.fState, 1); |
150 | 0 | return true; // Caller will next call the init function. |
151 | 0 | } else { |
152 | 0 | while (umtx_loadAcquire(uio.fState) == 1) { |
153 | | // Another thread is currently running the initialization. |
154 | | // Wait until it completes. |
155 | 0 | initCondition->wait(lock); |
156 | 0 | } |
157 | 0 | U_ASSERT(uio.fState == 2); |
158 | 0 | return false; |
159 | 0 | } |
160 | 0 | } |
161 | | |
162 | | |
163 | | // This function is called by the thread that ran an initialization function, |
164 | | // just after completing the function. |
165 | | // Some threads may be waiting on the condition, requiring the broadcast wakeup. |
166 | | // Some threads may be racing to test the fState variable outside of the mutex, |
167 | | // requiring the use of store/release when changing its value. |
168 | | |
169 | | U_COMMON_API void U_EXPORT2 |
170 | 0 | umtx_initImplPostInit(UInitOnce &uio) { |
171 | 0 | { |
172 | 0 | std::unique_lock<std::mutex> lock(*initMutex); |
173 | 0 | umtx_storeRelease(uio.fState, 2); |
174 | 0 | } |
175 | 0 | initCondition->notify_all(); |
176 | 0 | } |
177 | | |
178 | | U_NAMESPACE_END |
179 | | |
180 | | /************************************************************************************************* |
181 | | * |
182 | | * Deprecated functions for setting user mutexes. |
183 | | * |
184 | | *************************************************************************************************/ |
185 | | |
186 | | U_DEPRECATED void U_EXPORT2 |
187 | | u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *, |
188 | 0 | UMtxFn *, UMtxFn *, UErrorCode *status) { |
189 | 0 | if (U_SUCCESS(*status)) { |
190 | 0 | *status = U_UNSUPPORTED_ERROR; |
191 | 0 | } |
192 | 0 | return; |
193 | 0 | } |
194 | | |
195 | | |
196 | | |
197 | | U_DEPRECATED void U_EXPORT2 |
198 | | u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *, |
199 | 0 | UErrorCode *status) { |
200 | 0 | if (U_SUCCESS(*status)) { |
201 | 0 | *status = U_UNSUPPORTED_ERROR; |
202 | 0 | } |
203 | 0 | return; |
204 | 0 | } |