]>
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" | |
57a6839d | 27 | #include "cmemory.h" |
51004dcb | 28 | |
57a6839d A |
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 | ||
51004dcb A |
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 | ||
57a6839d A |
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) | |
51004dcb | 41 | |
f3c0d7a5 | 42 | #elif U_PLATFORM_USES_ONLY_WIN32_API |
51004dcb | 43 | |
57a6839d A |
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. | |
51004dcb A |
47 | #endif |
48 | ||
51004dcb | 49 | |
57a6839d A |
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. | |
51004dcb | 53 | // |
57a6839d A |
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. | |
51004dcb | 57 | // |
51004dcb | 58 | |
57a6839d | 59 | U_NAMESPACE_BEGIN |
51004dcb | 60 | |
57a6839d A |
61 | U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &uio) { |
62 | for (;;) { | |
63 | int32_t previousState = InterlockedCompareExchange( | |
f3c0d7a5 A |
64 | (LONG volatile *) // this is the type given in the API doc for this function. |
65 | &uio.fState, // Destination | |
57a6839d A |
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); | |
51004dcb | 84 | } |
51004dcb | 85 | } |
51004dcb A |
86 | } |
87 | ||
57a6839d A |
88 | // This function is called by the thread that ran an initialization function, |
89 | // just after completing the function. | |
51004dcb | 90 | |
57a6839d A |
91 | U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &uio) { |
92 | umtx_storeRelease(uio.fState, 2); | |
51004dcb | 93 | } |
51004dcb | 94 | |
57a6839d | 95 | U_NAMESPACE_END |
51004dcb | 96 | |
57a6839d A |
97 | static void winMutexInit(CRITICAL_SECTION *cs) { |
98 | InitializeCriticalSection(cs); | |
99 | return; | |
100 | } | |
51004dcb A |
101 | |
102 | U_CAPI void U_EXPORT2 | |
103 | umtx_lock(UMutex *mutex) { | |
104 | if (mutex == NULL) { | |
105 | mutex = &globalMutex; | |
106 | } | |
57a6839d A |
107 | CRITICAL_SECTION *cs = &mutex->fCS; |
108 | umtx_initOnce(mutex->fInitOnce, winMutexInit, cs); | |
109 | EnterCriticalSection(cs); | |
51004dcb A |
110 | } |
111 | ||
51004dcb A |
112 | U_CAPI void U_EXPORT2 |
113 | umtx_unlock(UMutex* mutex) | |
114 | { | |
115 | if (mutex == NULL) { | |
116 | mutex = &globalMutex; | |
117 | } | |
57a6839d | 118 | LeaveCriticalSection(&mutex->fCS); |
51004dcb A |
119 | } |
120 | ||
b331163b A |
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 | |
0f5d89e8 | 135 | umtx_condSignal(UConditionVar * /* condition */) { |
b331163b A |
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 | ||
57a6839d A |
178 | #elif U_PLATFORM_IMPLEMENTS_POSIX |
179 | ||
180 | //------------------------------------------------------------------------------------------- | |
51004dcb | 181 | // |
57a6839d | 182 | // POSIX specific definitions |
51004dcb | 183 | // |
57a6839d A |
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. | |
51004dcb | 191 | |
51004dcb A |
192 | U_CAPI void U_EXPORT2 |
193 | umtx_lock(UMutex *mutex) { | |
194 | if (mutex == NULL) { | |
195 | mutex = &globalMutex; | |
196 | } | |
57a6839d A |
197 | int sysErr = pthread_mutex_lock(&mutex->fMutex); |
198 | (void)sysErr; // Suppress unused variable warnings. | |
199 | U_ASSERT(sysErr == 0); | |
51004dcb A |
200 | } |
201 | ||
57a6839d | 202 | |
51004dcb A |
203 | U_CAPI void U_EXPORT2 |
204 | umtx_unlock(UMutex* mutex) | |
205 | { | |
206 | if (mutex == NULL) { | |
207 | mutex = &globalMutex; | |
208 | } | |
57a6839d A |
209 | int sysErr = pthread_mutex_unlock(&mutex->fMutex); |
210 | (void)sysErr; // Suppress unused variable warnings. | |
211 | U_ASSERT(sysErr == 0); | |
51004dcb A |
212 | } |
213 | ||
b331163b A |
214 | |
215 | U_CAPI void U_EXPORT2 | |
216 | umtx_condWait(UConditionVar *cond, UMutex *mutex) { | |
217 | if (mutex == NULL) { | |
218 | mutex = &globalMutex; | |
219 | } | |
220 | int sysErr = pthread_cond_wait(&cond->fCondition, &mutex->fMutex); | |
221 | (void)sysErr; | |
222 | U_ASSERT(sysErr == 0); | |
223 | } | |
224 | ||
225 | U_CAPI void U_EXPORT2 | |
226 | umtx_condBroadcast(UConditionVar *cond) { | |
227 | int sysErr = pthread_cond_broadcast(&cond->fCondition); | |
228 | (void)sysErr; | |
229 | U_ASSERT(sysErr == 0); | |
230 | } | |
231 | ||
232 | U_CAPI void U_EXPORT2 | |
233 | umtx_condSignal(UConditionVar *cond) { | |
234 | int sysErr = pthread_cond_signal(&cond->fCondition); | |
235 | (void)sysErr; | |
236 | U_ASSERT(sysErr == 0); | |
237 | } | |
238 | ||
239 | ||
240 | ||
57a6839d | 241 | U_NAMESPACE_BEGIN |
51004dcb | 242 | |
57a6839d A |
243 | static pthread_mutex_t initMutex = PTHREAD_MUTEX_INITIALIZER; |
244 | static pthread_cond_t initCondition = PTHREAD_COND_INITIALIZER; | |
51004dcb | 245 | |
51004dcb | 246 | |
57a6839d A |
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 | umtx_initImplPreInit(UInitOnce &uio) { | |
257 | pthread_mutex_lock(&initMutex); | |
258 | int32_t state = uio.fState; | |
259 | if (state == 0) { | |
260 | umtx_storeRelease(uio.fState, 1); | |
261 | pthread_mutex_unlock(&initMutex); | |
262 | return TRUE; // Caller will next call the init function. | |
263 | } else { | |
264 | while (uio.fState == 1) { | |
265 | // Another thread is currently running the initialization. | |
266 | // Wait until it completes. | |
267 | pthread_cond_wait(&initCondition, &initMutex); | |
268 | } | |
269 | pthread_mutex_unlock(&initMutex); | |
270 | U_ASSERT(uio.fState == 2); | |
271 | return FALSE; | |
51004dcb | 272 | } |
57a6839d | 273 | } |
51004dcb | 274 | |
51004dcb | 275 | |
57a6839d A |
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 | umtx_initImplPostInit(UInitOnce &uio) { | |
285 | pthread_mutex_lock(&initMutex); | |
286 | umtx_storeRelease(uio.fState, 2); | |
287 | pthread_cond_broadcast(&initCondition); | |
288 | pthread_mutex_unlock(&initMutex); | |
51004dcb A |
289 | } |
290 | ||
57a6839d | 291 | U_NAMESPACE_END |
51004dcb | 292 | |
57a6839d | 293 | // End of POSIX specific umutex implementation. |
51004dcb | 294 | |
57a6839d | 295 | #else // Platform #define chain. |
51004dcb | 296 | |
57a6839d | 297 | #error Unknown Platform |
51004dcb | 298 | |
57a6839d | 299 | #endif // Platform #define chain. |
51004dcb | 300 | |
51004dcb | 301 | |
57a6839d A |
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 | //-------------------------------------------------------------------------------- | |
51004dcb | 311 | |
57a6839d | 312 | #if defined U_NO_PLATFORM_ATOMICS |
51004dcb | 313 | static UMutex gIncDecMutex = U_MUTEX_INITIALIZER; |
51004dcb | 314 | |
57a6839d A |
315 | U_NAMESPACE_BEGIN |
316 | ||
317 | U_COMMON_API int32_t U_EXPORT2 | |
318 | umtx_atomic_inc(u_atomic_int32_t *p) { | |
51004dcb | 319 | int32_t retVal; |
57a6839d A |
320 | umtx_lock(&gIncDecMutex); |
321 | retVal = ++(*p); | |
322 | umtx_unlock(&gIncDecMutex); | |
51004dcb A |
323 | return retVal; |
324 | } | |
325 | ||
57a6839d A |
326 | |
327 | U_COMMON_API int32_t U_EXPORT2 | |
328 | umtx_atomic_dec(u_atomic_int32_t *p) { | |
51004dcb | 329 | int32_t retVal; |
57a6839d A |
330 | umtx_lock(&gIncDecMutex); |
331 | retVal = --(*p); | |
332 | umtx_unlock(&gIncDecMutex); | |
51004dcb A |
333 | return retVal; |
334 | } | |
335 | ||
57a6839d A |
336 | U_COMMON_API int32_t U_EXPORT2 |
337 | umtx_loadAcquire(u_atomic_int32_t &var) { | |
57a6839d | 338 | umtx_lock(&gIncDecMutex); |
2ca993e8 | 339 | int32_t val = var; |
57a6839d A |
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); | |
57a6839d | 347 | var = val; |
2ca993e8 | 348 | umtx_unlock(&gIncDecMutex); |
57a6839d | 349 | } |
51004dcb | 350 | |
57a6839d A |
351 | U_NAMESPACE_END |
352 | #endif | |
51004dcb | 353 | |
57a6839d A |
354 | //-------------------------------------------------------------------------- |
355 | // | |
356 | // Deprecated functions for setting user mutexes. | |
357 | // | |
358 | //-------------------------------------------------------------------------- | |
51004dcb | 359 | |
57a6839d A |
360 | U_DEPRECATED void U_EXPORT2 |
361 | u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *, | |
362 | UMtxFn *, UMtxFn *, UErrorCode *status) { | |
363 | if (U_SUCCESS(*status)) { | |
364 | *status = U_UNSUPPORTED_ERROR; | |
51004dcb | 365 | } |
57a6839d | 366 | return; |
51004dcb A |
367 | } |
368 | ||
369 | ||
57a6839d A |
370 | |
371 | U_DEPRECATED void U_EXPORT2 | |
372 | u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *, | |
373 | UErrorCode *status) { | |
374 | if (U_SUCCESS(*status)) { | |
375 | *status = U_UNSUPPORTED_ERROR; | |
376 | } | |
377 | return; | |
51004dcb | 378 | } |