/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 "cmemory.h" |
28 | | |
29 | | |
30 | | // The ICU global mutex. Used when ICU implementation code passes NULL for the mutex pointer. |
31 | | static UMutex globalMutex = U_MUTEX_INITIALIZER; |
32 | | |
33 | | /* |
34 | | * ICU Mutex wrappers. Wrap operating system mutexes, giving the rest of ICU a |
35 | | * platform independent set of mutex operations. For internal ICU use only. |
36 | | */ |
37 | | |
38 | | #if defined(U_USER_MUTEX_CPP) |
39 | | // Build time user mutex hook: #include "U_USER_MUTEX_CPP" |
40 | | #include U_MUTEX_XSTR(U_USER_MUTEX_CPP) |
41 | | |
42 | | #elif U_PLATFORM_USES_ONLY_WIN32_API |
43 | | |
44 | | #if defined U_NO_PLATFORM_ATOMICS |
45 | | #error ICU on Win32 requires support for low level atomic operations. |
46 | | // Visual Studio, gcc, clang are OK. Shouldn't get here. |
47 | | #endif |
48 | | |
49 | | |
50 | | // This function is called when a test of a UInitOnce::fState reveals that |
51 | | // initialization has not completed, that we either need to call the |
52 | | // function on this thread, or wait for some other thread to complete. |
53 | | // |
54 | | // The actual call to the init function is made inline by template code |
55 | | // that knows the C++ types involved. This function returns TRUE if |
56 | | // the caller needs to call the Init function. |
57 | | // |
58 | | |
59 | | U_NAMESPACE_BEGIN |
60 | | |
61 | | U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &uio) { |
62 | | for (;;) { |
63 | | int32_t previousState = InterlockedCompareExchange( |
64 | | (LONG volatile *) // this is the type given in the API doc for this function. |
65 | | &uio.fState, // Destination |
66 | | 1, // Exchange Value |
67 | | 0); // Compare value |
68 | | |
69 | | if (previousState == 0) { |
70 | | return true; // Caller will next call the init function. |
71 | | // Current state == 1. |
72 | | } else if (previousState == 2) { |
73 | | // Another thread already completed the initialization. |
74 | | // We can simply return FALSE, indicating no |
75 | | // further action is needed by the caller. |
76 | | return FALSE; |
77 | | } else { |
78 | | // Another thread is currently running the initialization. |
79 | | // Wait until it completes. |
80 | | do { |
81 | | Sleep(1); |
82 | | previousState = umtx_loadAcquire(uio.fState); |
83 | | } while (previousState == 1); |
84 | | } |
85 | | } |
86 | | } |
87 | | |
88 | | // This function is called by the thread that ran an initialization function, |
89 | | // just after completing the function. |
90 | | |
91 | | U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &uio) { |
92 | | umtx_storeRelease(uio.fState, 2); |
93 | | } |
94 | | |
95 | | U_NAMESPACE_END |
96 | | |
97 | | static void winMutexInit(CRITICAL_SECTION *cs) { |
98 | | InitializeCriticalSection(cs); |
99 | | return; |
100 | | } |
101 | | |
102 | | U_CAPI void U_EXPORT2 |
103 | | umtx_lock(UMutex *mutex) { |
104 | | if (mutex == NULL) { |
105 | | mutex = &globalMutex; |
106 | | } |
107 | | CRITICAL_SECTION *cs = &mutex->fCS; |
108 | | umtx_initOnce(mutex->fInitOnce, winMutexInit, cs); |
109 | | EnterCriticalSection(cs); |
110 | | } |
111 | | |
112 | | U_CAPI void U_EXPORT2 |
113 | | umtx_unlock(UMutex* mutex) |
114 | | { |
115 | | if (mutex == NULL) { |
116 | | mutex = &globalMutex; |
117 | | } |
118 | | LeaveCriticalSection(&mutex->fCS); |
119 | | } |
120 | | |
121 | | |
122 | | U_CAPI void U_EXPORT2 |
123 | | umtx_condBroadcast(UConditionVar *condition) { |
124 | | // We require that the associated mutex be held by the caller, |
125 | | // so access to fWaitCount is protected and safe. No other thread can |
126 | | // call condWait() while we are here. |
127 | | if (condition->fWaitCount == 0) { |
128 | | return; |
129 | | } |
130 | | ResetEvent(condition->fExitGate); |
131 | | SetEvent(condition->fEntryGate); |
132 | | } |
133 | | |
134 | | U_CAPI void U_EXPORT2 |
135 | | umtx_condSignal(UConditionVar *condition) { |
136 | | // Function not implemented. There is no immediate requirement from ICU to have it. |
137 | | // Once ICU drops support for Windows XP and Server 2003, ICU Condition Variables will be |
138 | | // changed to be thin wrappers on native Windows CONDITION_VARIABLEs, and this function |
139 | | // becomes trivial to provide. |
140 | | U_ASSERT(FALSE); |
141 | | } |
142 | | |
143 | | U_CAPI void U_EXPORT2 |
144 | | umtx_condWait(UConditionVar *condition, UMutex *mutex) { |
145 | | if (condition->fEntryGate == NULL) { |
146 | | // Note: because the associated mutex must be locked when calling |
147 | | // wait, we know that there can not be multiple threads |
148 | | // running here with the same condition variable. |
149 | | // Meaning that lazy initialization is safe. |
150 | | U_ASSERT(condition->fExitGate == NULL); |
151 | | condition->fEntryGate = CreateEvent(NULL, // Security Attributes |
152 | | TRUE, // Manual Reset |
153 | | FALSE, // Initially reset |
154 | | NULL); // Name. |
155 | | U_ASSERT(condition->fEntryGate != NULL); |
156 | | condition->fExitGate = CreateEvent(NULL, TRUE, TRUE, NULL); |
157 | | U_ASSERT(condition->fExitGate != NULL); |
158 | | } |
159 | | |
160 | | condition->fWaitCount++; |
161 | | umtx_unlock(mutex); |
162 | | WaitForSingleObject(condition->fEntryGate, INFINITE); |
163 | | umtx_lock(mutex); |
164 | | condition->fWaitCount--; |
165 | | if (condition->fWaitCount == 0) { |
166 | | // All threads that were waiting at the entry gate have woken up |
167 | | // and moved through. Shut the entry gate and open the exit gate. |
168 | | ResetEvent(condition->fEntryGate); |
169 | | SetEvent(condition->fExitGate); |
170 | | } else { |
171 | | umtx_unlock(mutex); |
172 | | WaitForSingleObject(condition->fExitGate, INFINITE); |
173 | | umtx_lock(mutex); |
174 | | } |
175 | | } |
176 | | |
177 | | |
178 | | #elif U_PLATFORM_IMPLEMENTS_POSIX |
179 | | |
180 | | //------------------------------------------------------------------------------------------- |
181 | | // |
182 | | // POSIX specific definitions |
183 | | // |
184 | | //------------------------------------------------------------------------------------------- |
185 | | |
186 | | # include <pthread.h> |
187 | | |
188 | | // Each UMutex consists of a pthread_mutex_t. |
189 | | // All are statically initialized and ready for use. |
190 | | // There is no runtime mutex initialization code needed. |
191 | | |
192 | | U_CAPI void U_EXPORT2 |
193 | 20.4k | umtx_lock(UMutex *mutex) { |
194 | 20.4k | if (mutex == NULL) { |
195 | 13.3k | mutex = &globalMutex; |
196 | 13.3k | } |
197 | 20.4k | int sysErr = pthread_mutex_lock(&mutex->fMutex); |
198 | 20.4k | (void)sysErr; // Suppress unused variable warnings. |
199 | 20.4k | U_ASSERT(sysErr == 0); |
200 | 20.4k | } |
201 | | |
202 | | |
203 | | U_CAPI void U_EXPORT2 |
204 | | umtx_unlock(UMutex* mutex) |
205 | 20.4k | { |
206 | 20.4k | if (mutex == NULL) { |
207 | 13.3k | mutex = &globalMutex; |
208 | 13.3k | } |
209 | 20.4k | int sysErr = pthread_mutex_unlock(&mutex->fMutex); |
210 | 20.4k | (void)sysErr; // Suppress unused variable warnings. |
211 | 20.4k | U_ASSERT(sysErr == 0); |
212 | 20.4k | } |
213 | | |
214 | | |
215 | | U_CAPI void U_EXPORT2 |
216 | 0 | umtx_condWait(UConditionVar *cond, UMutex *mutex) { |
217 | 0 | if (mutex == NULL) { |
218 | 0 | mutex = &globalMutex; |
219 | 0 | } |
220 | 0 | int sysErr = pthread_cond_wait(&cond->fCondition, &mutex->fMutex); |
221 | 0 | (void)sysErr; |
222 | 0 | U_ASSERT(sysErr == 0); |
223 | 0 | } |
224 | | |
225 | | U_CAPI void U_EXPORT2 |
226 | 0 | umtx_condBroadcast(UConditionVar *cond) { |
227 | 0 | int sysErr = pthread_cond_broadcast(&cond->fCondition); |
228 | 0 | (void)sysErr; |
229 | 0 | U_ASSERT(sysErr == 0); |
230 | 0 | } |
231 | | |
232 | | U_CAPI void U_EXPORT2 |
233 | 0 | umtx_condSignal(UConditionVar *cond) { |
234 | 0 | int sysErr = pthread_cond_signal(&cond->fCondition); |
235 | 0 | (void)sysErr; |
236 | 0 | U_ASSERT(sysErr == 0); |
237 | 0 | } |
238 | | |
239 | | |
240 | | |
241 | | U_NAMESPACE_BEGIN |
242 | | |
243 | | static pthread_mutex_t initMutex = PTHREAD_MUTEX_INITIALIZER; |
244 | | static pthread_cond_t initCondition = PTHREAD_COND_INITIALIZER; |
245 | | |
246 | | |
247 | | // This function is called when a test of a UInitOnce::fState reveals that |
248 | | // initialization has not completed, that we either need to call the |
249 | | // function on this thread, or wait for some other thread to complete. |
250 | | // |
251 | | // The actual call to the init function is made inline by template code |
252 | | // that knows the C++ types involved. This function returns TRUE if |
253 | | // the caller needs to call the Init function. |
254 | | // |
255 | | U_COMMON_API UBool U_EXPORT2 |
256 | 2.38k | umtx_initImplPreInit(UInitOnce &uio) { |
257 | 2.38k | pthread_mutex_lock(&initMutex); |
258 | 2.38k | int32_t state = uio.fState; |
259 | 2.38k | if (state == 0) { |
260 | 2.38k | umtx_storeRelease(uio.fState, 1); |
261 | 2.38k | pthread_mutex_unlock(&initMutex); |
262 | 2.38k | return TRUE; // Caller will next call the init function. |
263 | 2.38k | } else { |
264 | 0 | while (uio.fState == 1) { |
265 | | // Another thread is currently running the initialization. |
266 | | // Wait until it completes. |
267 | 0 | pthread_cond_wait(&initCondition, &initMutex); |
268 | 0 | } |
269 | 0 | pthread_mutex_unlock(&initMutex); |
270 | 0 | U_ASSERT(uio.fState == 2); |
271 | 0 | return FALSE; |
272 | 0 | } |
273 | 2.38k | } |
274 | | |
275 | | |
276 | | |
277 | | // This function is called by the thread that ran an initialization function, |
278 | | // just after completing the function. |
279 | | // Some threads may be waiting on the condition, requiring the broadcast wakeup. |
280 | | // Some threads may be racing to test the fState variable outside of the mutex, |
281 | | // requiring the use of store/release when changing its value. |
282 | | |
283 | | U_COMMON_API void U_EXPORT2 |
284 | 2.38k | umtx_initImplPostInit(UInitOnce &uio) { |
285 | 2.38k | pthread_mutex_lock(&initMutex); |
286 | 2.38k | umtx_storeRelease(uio.fState, 2); |
287 | 2.38k | pthread_cond_broadcast(&initCondition); |
288 | 2.38k | pthread_mutex_unlock(&initMutex); |
289 | 2.38k | } |
290 | | |
291 | | U_NAMESPACE_END |
292 | | |
293 | | // End of POSIX specific umutex implementation. |
294 | | |
295 | | #else // Platform #define chain. |
296 | | |
297 | | #error Unknown Platform |
298 | | |
299 | | #endif // Platform #define chain. |
300 | | |
301 | | |
302 | | //------------------------------------------------------------------------------- |
303 | | // |
304 | | // Atomic Operations, out-of-line versions. |
305 | | // These are conditional, only defined if better versions |
306 | | // were not available for the platform. |
307 | | // |
308 | | // These versions are platform neutral. |
309 | | // |
310 | | //-------------------------------------------------------------------------------- |
311 | | |
312 | | #if defined U_NO_PLATFORM_ATOMICS |
313 | | static UMutex gIncDecMutex = U_MUTEX_INITIALIZER; |
314 | | |
315 | | U_NAMESPACE_BEGIN |
316 | | |
317 | | U_COMMON_API int32_t U_EXPORT2 |
318 | | umtx_atomic_inc(u_atomic_int32_t *p) { |
319 | | int32_t retVal; |
320 | | umtx_lock(&gIncDecMutex); |
321 | | retVal = ++(*p); |
322 | | umtx_unlock(&gIncDecMutex); |
323 | | return retVal; |
324 | | } |
325 | | |
326 | | |
327 | | U_COMMON_API int32_t U_EXPORT2 |
328 | | umtx_atomic_dec(u_atomic_int32_t *p) { |
329 | | int32_t retVal; |
330 | | umtx_lock(&gIncDecMutex); |
331 | | retVal = --(*p); |
332 | | umtx_unlock(&gIncDecMutex); |
333 | | return retVal; |
334 | | } |
335 | | |
336 | | U_COMMON_API int32_t U_EXPORT2 |
337 | | umtx_loadAcquire(u_atomic_int32_t &var) { |
338 | | umtx_lock(&gIncDecMutex); |
339 | | int32_t val = var; |
340 | | umtx_unlock(&gIncDecMutex); |
341 | | return val; |
342 | | } |
343 | | |
344 | | U_COMMON_API void U_EXPORT2 |
345 | | umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { |
346 | | umtx_lock(&gIncDecMutex); |
347 | | var = val; |
348 | | umtx_unlock(&gIncDecMutex); |
349 | | } |
350 | | |
351 | | U_NAMESPACE_END |
352 | | #endif |
353 | | |
354 | | //-------------------------------------------------------------------------- |
355 | | // |
356 | | // Deprecated functions for setting user mutexes. |
357 | | // |
358 | | //-------------------------------------------------------------------------- |
359 | | |
360 | | U_DEPRECATED void U_EXPORT2 |
361 | | u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *, |
362 | 0 | UMtxFn *, UMtxFn *, UErrorCode *status) { |
363 | 0 | if (U_SUCCESS(*status)) { |
364 | 0 | *status = U_UNSUPPORTED_ERROR; |
365 | 0 | } |
366 | 0 | return; |
367 | 0 | } |
368 | | |
369 | | |
370 | | |
371 | | U_DEPRECATED void U_EXPORT2 |
372 | | u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *, |
373 | 0 | UErrorCode *status) { |
374 | 0 | if (U_SUCCESS(*status)) { |
375 | 0 | *status = U_UNSUPPORTED_ERROR; |
376 | 0 | } |
377 | 0 | return; |
378 | 0 | } |