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 #ifndef __wasi__
47 namespace {
48 std::mutex *initMutex;
49 std::condition_variable *initCondition;
50 
51 // The ICU global mutex.
52 // Used when ICU implementation code passes nullptr for the mutex pointer.
53 UMutex globalMutex;
54 
55 std::once_flag initFlag;
56 std::once_flag *pInitFlag = &initFlag;
57 
58 }  // Anonymous namespace
59 #endif
60 
61 U_CDECL_BEGIN
umtx_cleanup()62 static UBool U_CALLCONV umtx_cleanup() {
63 #ifndef __wasi__
64     initMutex->~mutex();
65     initCondition->~condition_variable();
66     UMutex::cleanup();
67 
68     // Reset the once_flag, by destructing it and creating a fresh one in its place.
69     // Do not use this trick anywhere else in ICU; use umtx_initOnce, not std::call_once().
70     pInitFlag->~once_flag();
71     pInitFlag = new(&initFlag) std::once_flag();
72 #endif
73     return true;
74 }
75 
umtx_init()76 static void U_CALLCONV umtx_init() {
77 #ifndef __wasi__
78     initMutex = STATIC_NEW(std::mutex);
79     initCondition = STATIC_NEW(std::condition_variable);
80     ucln_common_registerCleanup(UCLN_COMMON_MUTEX, umtx_cleanup);
81 #endif
82 }
83 U_CDECL_END
84 
85 
86 #ifndef __wasi__
getMutex()87 std::mutex *UMutex::getMutex() {
88     std::mutex *retPtr = fMutex.load(std::memory_order_acquire);
89     if (retPtr == nullptr) {
90         std::call_once(*pInitFlag, umtx_init);
91         std::lock_guard<std::mutex> guard(*initMutex);
92         retPtr = fMutex.load(std::memory_order_acquire);
93         if (retPtr == nullptr) {
94             fMutex = new(fStorage) std::mutex();
95             retPtr = fMutex;
96             fListLink = gListHead;
97             gListHead = this;
98         }
99     }
100     U_ASSERT(retPtr != nullptr);
101     return retPtr;
102 }
103 #endif
104 
105 UMutex *UMutex::gListHead = nullptr;
106 
cleanup()107 void UMutex::cleanup() {
108     UMutex *next = nullptr;
109     for (UMutex *m = gListHead; m != nullptr; m = next) {
110 #ifndef __wasi__
111         (*m->fMutex).~mutex();
112         m->fMutex = nullptr;
113 #endif
114         next = m->fListLink;
115         m->fListLink = nullptr;
116     }
117     gListHead = nullptr;
118 }
119 
120 
121 U_CAPI void  U_EXPORT2
umtx_lock(UMutex * mutex)122 umtx_lock(UMutex *mutex) {
123 #ifndef __wasi__
124     if (mutex == nullptr) {
125         mutex = &globalMutex;
126     }
127     mutex->lock();
128 #endif
129 }
130 
131 
132 U_CAPI void  U_EXPORT2
umtx_unlock(UMutex * mutex)133 umtx_unlock(UMutex* mutex)
134 {
135 #ifndef __wasi__
136     if (mutex == nullptr) {
137         mutex = &globalMutex;
138     }
139     mutex->unlock();
140 #endif
141 }
142 
143 
144 /*************************************************************************************************
145  *
146  *  UInitOnce Implementation
147  *
148  *************************************************************************************************/
149 
150 // This function is called when a test of a UInitOnce::fState reveals that
151 //   initialization has not completed, that we either need to call the init
152 //   function on this thread, or wait for some other thread to complete.
153 //
154 // The actual call to the init function is made inline by template code
155 //   that knows the C++ types involved. This function returns true if
156 //   the caller needs to call the Init function.
157 //
158 U_COMMON_API UBool U_EXPORT2
umtx_initImplPreInit(UInitOnce & uio)159 umtx_initImplPreInit(UInitOnce &uio) {
160 #ifndef __wasi__
161     std::call_once(*pInitFlag, umtx_init);
162     std::unique_lock<std::mutex> lock(*initMutex);
163 #endif
164     if (umtx_loadAcquire(uio.fState) == 0) {
165         umtx_storeRelease(uio.fState, 1);
166         return true;      // Caller will next call the init function.
167     } else {
168 #ifndef __wasi__
169         while (umtx_loadAcquire(uio.fState) == 1) {
170             // Another thread is currently running the initialization.
171             // Wait until it completes.
172             initCondition->wait(lock);
173         }
174         U_ASSERT(uio.fState == 2);
175 #endif
176         return false;
177     }
178 }
179 
180 
181 // This function is called by the thread that ran an initialization function,
182 // just after completing the function.
183 //   Some threads may be waiting on the condition, requiring the broadcast wakeup.
184 //   Some threads may be racing to test the fState variable outside of the mutex,
185 //   requiring the use of store/release when changing its value.
186 
187 U_COMMON_API void U_EXPORT2
umtx_initImplPostInit(UInitOnce & uio)188 umtx_initImplPostInit(UInitOnce &uio) {
189 #ifndef __wasi__
190     {
191         std::unique_lock<std::mutex> lock(*initMutex);
192         umtx_storeRelease(uio.fState, 2);
193     }
194     initCondition->notify_all();
195 #endif
196 }
197 
198 U_NAMESPACE_END
199 
200 /*************************************************************************************************
201  *
202  *  Deprecated functions for setting user mutexes.
203  *
204  *************************************************************************************************/
205 
206 U_DEPRECATED void U_EXPORT2
u_setMutexFunctions(const void *,UMtxInitFn *,UMtxFn *,UMtxFn *,UMtxFn *,UErrorCode * status)207 u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *,
208                     UMtxFn *,  UMtxFn *, UErrorCode *status) {
209     if (U_SUCCESS(*status)) {
210         *status = U_UNSUPPORTED_ERROR;
211     }
212     return;
213 }
214 
215 
216 
217 U_DEPRECATED void U_EXPORT2
u_setAtomicIncDecFunctions(const void *,UMtxAtomicFn *,UMtxAtomicFn *,UErrorCode * status)218 u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *,
219                            UErrorCode *status) {
220     if (U_SUCCESS(*status)) {
221         *status = U_UNSUPPORTED_ERROR;
222     }
223     return;
224 }
225