]>
Commit | Line | Data |
---|---|---|
f3c0d7a5 A |
1 | // © 2016 and later: Unicode, Inc. and others. |
2 | // License & terms of use: http://www.unicode.org/copyright.html | |
51004dcb A |
3 | /* |
4 | ****************************************************************************** | |
5 | * | |
f3c0d7a5 | 6 | * Copyright (C) 1997-2016, International Business Machines |
51004dcb A |
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 | ||
57a6839d A |
23 | #include "umutex.h" |
24 | ||
51004dcb A |
25 | #include "unicode/utypes.h" |
26 | #include "uassert.h" | |
340931cb | 27 | #include "ucln_cmn.h" |
57a6839d | 28 | #include "cmemory.h" |
51004dcb | 29 | |
3d1f044b | 30 | U_NAMESPACE_BEGIN |
57a6839d | 31 | |
51004dcb | 32 | |
57a6839d | 33 | #if defined(U_USER_MUTEX_CPP) |
3d1f044b A |
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 | |
51004dcb A |
37 | #endif |
38 | ||
340931cb | 39 | |
3d1f044b A |
40 | /************************************************************************************************* |
41 | * | |
42 | * ICU Mutex wrappers. | |
43 | * | |
44 | *************************************************************************************************/ | |
51004dcb | 45 | |
340931cb A |
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; | |
b331163b A |
108 | } |
109 | ||
340931cb | 110 | |
51004dcb A |
111 | U_CAPI void U_EXPORT2 |
112 | umtx_lock(UMutex *mutex) { | |
3d1f044b | 113 | if (mutex == nullptr) { |
340931cb | 114 | mutex = &globalMutex; |
51004dcb | 115 | } |
340931cb | 116 | mutex->lock(); |
51004dcb A |
117 | } |
118 | ||
57a6839d | 119 | |
51004dcb A |
120 | U_CAPI void U_EXPORT2 |
121 | umtx_unlock(UMutex* mutex) | |
122 | { | |
3d1f044b | 123 | if (mutex == nullptr) { |
340931cb | 124 | mutex = &globalMutex; |
51004dcb | 125 | } |
340931cb | 126 | mutex->unlock(); |
51004dcb A |
127 | } |
128 | ||
b331163b | 129 | |
3d1f044b A |
130 | /************************************************************************************************* |
131 | * | |
132 | * UInitOnce Implementation | |
133 | * | |
134 | *************************************************************************************************/ | |
b331163b | 135 | |
57a6839d | 136 | // This function is called when a test of a UInitOnce::fState reveals that |
3d1f044b | 137 | // initialization has not completed, that we either need to call the init |
57a6839d A |
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 | |
3d1f044b | 141 | // that knows the C++ types involved. This function returns true if |
57a6839d A |
142 | // the caller needs to call the Init function. |
143 | // | |
144 | U_COMMON_API UBool U_EXPORT2 | |
145 | umtx_initImplPreInit(UInitOnce &uio) { | |
340931cb A |
146 | std::call_once(*pInitFlag, umtx_init); |
147 | std::unique_lock<std::mutex> lock(*initMutex); | |
3d1f044b | 148 | if (umtx_loadAcquire(uio.fState) == 0) { |
57a6839d | 149 | umtx_storeRelease(uio.fState, 1); |
3d1f044b | 150 | return true; // Caller will next call the init function. |
57a6839d | 151 | } else { |
3d1f044b | 152 | while (umtx_loadAcquire(uio.fState) == 1) { |
57a6839d A |
153 | // Another thread is currently running the initialization. |
154 | // Wait until it completes. | |
340931cb | 155 | initCondition->wait(lock); |
57a6839d | 156 | } |
57a6839d | 157 | U_ASSERT(uio.fState == 2); |
3d1f044b | 158 | return false; |
51004dcb | 159 | } |
57a6839d | 160 | } |
51004dcb | 161 | |
51004dcb | 162 | |
57a6839d A |
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) { | |
3d1f044b | 171 | { |
340931cb | 172 | std::unique_lock<std::mutex> lock(*initMutex); |
3d1f044b A |
173 | umtx_storeRelease(uio.fState, 2); |
174 | } | |
340931cb | 175 | initCondition->notify_all(); |
57a6839d | 176 | } |
51004dcb | 177 | |
57a6839d | 178 | U_NAMESPACE_END |
51004dcb | 179 | |
3d1f044b A |
180 | /************************************************************************************************* |
181 | * | |
182 | * Deprecated functions for setting user mutexes. | |
183 | * | |
184 | *************************************************************************************************/ | |
51004dcb | 185 | |
57a6839d A |
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; | |
51004dcb | 191 | } |
57a6839d | 192 | return; |
51004dcb A |
193 | } |
194 | ||
195 | ||
57a6839d A |
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; | |
51004dcb | 204 | } |