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 | static UBool U_CALLCONV umtx_cleanup() { |
61 | initMutex->~mutex(); |
62 | initCondition->~condition_variable(); |
63 | 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 | pInitFlag->~once_flag(); |
68 | pInitFlag = new(&initFlag) std::once_flag(); |
69 | return true; |
70 | } |
71 | |
72 | static void U_CALLCONV umtx_init() { |
73 | initMutex = STATIC_NEW(std::mutex); |
74 | initCondition = STATIC_NEW(std::condition_variable); |
75 | ucln_common_registerCleanup(UCLN_COMMON_MUTEX, umtx_cleanup); |
76 | } |
77 | U_CDECL_END |
78 | |
79 | |
80 | std::mutex *UMutex::getMutex() { |
81 | std::mutex *retPtr = fMutex.load(std::memory_order_acquire); |
82 | if (retPtr == nullptr) { |
83 | std::call_once(*pInitFlag, umtx_init); |
84 | std::lock_guard<std::mutex> guard(*initMutex); |
85 | retPtr = fMutex.load(std::memory_order_acquire); |
86 | if (retPtr == nullptr) { |
87 | fMutex = new(fStorage) std::mutex(); |
88 | retPtr = fMutex; |
89 | fListLink = gListHead; |
90 | gListHead = this; |
91 | } |
92 | } |
93 | U_ASSERT(retPtr != nullptr); |
94 | return retPtr; |
95 | } |
96 | |
97 | UMutex *UMutex::gListHead = nullptr; |
98 | |
99 | void UMutex::cleanup() { |
100 | UMutex *next = nullptr; |
101 | for (UMutex *m = gListHead; m != nullptr; m = next) { |
102 | (*m->fMutex).~mutex(); |
103 | m->fMutex = nullptr; |
104 | next = m->fListLink; |
105 | m->fListLink = nullptr; |
106 | } |
107 | gListHead = nullptr; |
108 | } |
109 | |
110 | |
111 | U_CAPI void U_EXPORT2 |
112 | umtx_lock(UMutex *mutex) { |
113 | if (mutex == nullptr) { |
114 | mutex = &globalMutex; |
115 | } |
116 | mutex->lock(); |
117 | } |
118 | |
119 | |
120 | U_CAPI void U_EXPORT2 |
121 | umtx_unlock(UMutex* mutex) |
122 | { |
123 | if (mutex == nullptr) { |
124 | mutex = &globalMutex; |
125 | } |
126 | mutex->unlock(); |
127 | } |
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 | umtx_initImplPreInit(UInitOnce &uio) { |
146 | std::call_once(*pInitFlag, umtx_init); |
147 | std::unique_lock<std::mutex> lock(*initMutex); |
148 | if (umtx_loadAcquire(uio.fState) == 0) { |
149 | umtx_storeRelease(uio.fState, 1); |
150 | return true; // Caller will next call the init function. |
151 | } else { |
152 | while (umtx_loadAcquire(uio.fState) == 1) { |
153 | // Another thread is currently running the initialization. |
154 | // Wait until it completes. |
155 | initCondition->wait(lock); |
156 | } |
157 | U_ASSERT(uio.fState == 2); |
158 | return false; |
159 | } |
160 | } |
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 | umtx_initImplPostInit(UInitOnce &uio) { |
171 | { |
172 | std::unique_lock<std::mutex> lock(*initMutex); |
173 | umtx_storeRelease(uio.fState, 2); |
174 | } |
175 | initCondition->notify_all(); |
176 | } |
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 | UMtxFn *, UMtxFn *, UErrorCode *status) { |
189 | if (U_SUCCESS(*status)) { |
190 | *status = U_UNSUPPORTED_ERROR; |
191 | } |
192 | return; |
193 | } |
194 | |
195 | |
196 | |
197 | U_DEPRECATED void U_EXPORT2 |
198 | u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *, |
199 | UErrorCode *status) { |
200 | if (U_SUCCESS(*status)) { |
201 | *status = U_UNSUPPORTED_ERROR; |
202 | } |
203 | return; |
204 | } |
205 | |