Line data Source code
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 84 : umtx_lock(UMutex *mutex) {
194 84 : if (mutex == NULL) {
195 72 : mutex = &globalMutex;
196 : }
197 84 : int sysErr = pthread_mutex_lock(&mutex->fMutex);
198 : (void)sysErr; // Suppress unused variable warnings.
199 84 : U_ASSERT(sysErr == 0);
200 84 : }
201 :
202 :
203 : U_CAPI void U_EXPORT2
204 84 : umtx_unlock(UMutex* mutex)
205 : {
206 84 : if (mutex == NULL) {
207 72 : mutex = &globalMutex;
208 : }
209 84 : int sysErr = pthread_mutex_unlock(&mutex->fMutex);
210 : (void)sysErr; // Suppress unused variable warnings.
211 84 : U_ASSERT(sysErr == 0);
212 84 : }
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 : }
220 0 : int sysErr = pthread_cond_wait(&cond->fCondition, &mutex->fMutex);
221 : (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 : (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 : (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 15 : umtx_initImplPreInit(UInitOnce &uio) {
257 15 : pthread_mutex_lock(&initMutex);
258 15 : int32_t state = uio.fState;
259 15 : if (state == 0) {
260 15 : umtx_storeRelease(uio.fState, 1);
261 15 : pthread_mutex_unlock(&initMutex);
262 15 : return TRUE; // Caller will next call the init function.
263 : } 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 : }
269 0 : pthread_mutex_unlock(&initMutex);
270 0 : U_ASSERT(uio.fState == 2);
271 0 : 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 15 : umtx_initImplPostInit(UInitOnce &uio) {
285 15 : pthread_mutex_lock(&initMutex);
286 15 : umtx_storeRelease(uio.fState, 2);
287 15 : pthread_cond_broadcast(&initCondition);
288 15 : pthread_mutex_unlock(&initMutex);
289 15 : }
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 0 : u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *,
362 : UMtxFn *, UMtxFn *, UErrorCode *status) {
363 0 : if (U_SUCCESS(*status)) {
364 0 : *status = U_UNSUPPORTED_ERROR;
365 : }
366 0 : return;
367 : }
368 :
369 :
370 :
371 : U_DEPRECATED void U_EXPORT2
372 0 : u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *,
373 : UErrorCode *status) {
374 0 : if (U_SUCCESS(*status)) {
375 0 : *status = U_UNSUPPORTED_ERROR;
376 : }
377 0 : return;
378 : }
|