• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
umtx_initImplPreInit(UInitOnce & uio)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 
umtx_initImplPostInit(UInitOnce & uio)91 U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &uio) {
92     umtx_storeRelease(uio.fState, 2);
93 }
94 
95 U_NAMESPACE_END
96 
winMutexInit(CRITICAL_SECTION * cs)97 static void winMutexInit(CRITICAL_SECTION *cs) {
98     InitializeCriticalSection(cs);
99     return;
100 }
101 
102 U_CAPI void  U_EXPORT2
umtx_lock(UMutex * mutex)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
umtx_unlock(UMutex * mutex)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
umtx_condBroadcast(UConditionVar * condition)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
umtx_condSignal(UConditionVar *)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
umtx_condWait(UConditionVar * condition,UMutex * mutex)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
umtx_lock(UMutex * mutex)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
umtx_unlock(UMutex * mutex)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
umtx_condWait(UConditionVar * cond,UMutex * mutex)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
umtx_condBroadcast(UConditionVar * cond)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
umtx_condSignal(UConditionVar * cond)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
umtx_initImplPreInit(UInitOnce & uio)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
umtx_initImplPostInit(UInitOnce & uio)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
umtx_atomic_inc(u_atomic_int32_t * p)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
umtx_atomic_dec(u_atomic_int32_t * p)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
umtx_loadAcquire(u_atomic_int32_t & var)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
umtx_storeRelease(u_atomic_int32_t & var,int32_t val)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
u_setMutexFunctions(const void *,UMtxInitFn *,UMtxFn *,UMtxFn *,UMtxFn *,UErrorCode * status)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
u_setAtomicIncDecFunctions(const void *,UMtxAtomicFn *,UMtxAtomicFn *,UErrorCode * status)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 }
379