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