]>
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 "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 | umtx_lock(UMutex *mutex) { | |
194 | if (mutex == NULL) { | |
195 | mutex = &globalMutex; | |
196 | } | |
197 | int sysErr = pthread_mutex_lock(&mutex->fMutex); | |
198 | (void)sysErr; // Suppress unused variable warnings. | |
199 | U_ASSERT(sysErr == 0); | |
200 | } | |
201 | ||
202 | ||
203 | U_CAPI void U_EXPORT2 | |
204 | umtx_unlock(UMutex* mutex) | |
205 | { | |
206 | if (mutex == NULL) { | |
207 | mutex = &globalMutex; | |
208 | } | |
209 | int sysErr = pthread_mutex_unlock(&mutex->fMutex); | |
210 | (void)sysErr; // Suppress unused variable warnings. | |
211 | U_ASSERT(sysErr == 0); | |
212 | } | |
213 | ||
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 | ||
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 | 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; | |
272 | } | |
273 | } | |
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 | 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); | |
289 | } | |
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 | UMtxFn *, UMtxFn *, UErrorCode *status) { | |
363 | if (U_SUCCESS(*status)) { | |
364 | *status = U_UNSUPPORTED_ERROR; | |
365 | } | |
366 | return; | |
367 | } | |
368 | ||
369 | ||
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; | |
378 | } |