]>
Commit | Line | Data |
---|---|---|
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 | } |