• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 ******************************************************************************
3 *
4 *   Copyright (C) 1997-2015, International Business Machines
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 
21 #include "umutex.h"
22 
23 #include "unicode/utypes.h"
24 #include "uassert.h"
25 #include "cmemory.h"
26 
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 
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 
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)
39 
40 #elif U_PLATFORM_HAS_WIN32_API
41 
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 //-------------------------------------------------------------------------------------------
50 
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.
54 #endif
55 
56 
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.
60 //
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.
64 //
65 
66 U_NAMESPACE_BEGIN
67 
umtx_initImplPreInit(UInitOnce & uio)68 U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &uio) {
69     for (;;) {
70         int32_t previousState = InterlockedCompareExchange(
71 #if (U_PLATFORM == U_PF_MINGW) || (U_PLATFORM == U_PF_CYGWIN) || defined(__clang__)
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);
93         }
94     }
95 }
96 
97 // This function is called by the thread that ran an initialization function,
98 // just after completing the function.
99 
umtx_initImplPostInit(UInitOnce & uio)100 U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &uio) {
101     umtx_storeRelease(uio.fState, 2);
102 }
103 
104 U_NAMESPACE_END
105 
winMutexInit(CRITICAL_SECTION * cs)106 static void winMutexInit(CRITICAL_SECTION *cs) {
107     InitializeCriticalSection(cs);
108     return;
109 }
110 
111 U_CAPI void  U_EXPORT2
umtx_lock(UMutex * mutex)112 umtx_lock(UMutex *mutex) {
113     if (mutex == NULL) {
114         mutex = &globalMutex;
115     }
116     CRITICAL_SECTION *cs = &mutex->fCS;
117     umtx_initOnce(mutex->fInitOnce, winMutexInit, cs);
118     EnterCriticalSection(cs);
119 }
120 
121 U_CAPI void  U_EXPORT2
umtx_unlock(UMutex * mutex)122 umtx_unlock(UMutex* mutex)
123 {
124     if (mutex == NULL) {
125         mutex = &globalMutex;
126     }
127     LeaveCriticalSection(&mutex->fCS);
128 }
129 
130 
131 U_CAPI void U_EXPORT2
umtx_condBroadcast(UConditionVar * condition)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
umtx_condSignal(UConditionVar * condition)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
umtx_condWait(UConditionVar * condition,UMutex * mutex)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 
187 #elif U_PLATFORM_IMPLEMENTS_POSIX
188 
189 //-------------------------------------------------------------------------------------------
190 //
191 //  POSIX specific definitions
192 //
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.
200 
201 U_CAPI void  U_EXPORT2
umtx_lock(UMutex * mutex)202 umtx_lock(UMutex *mutex) {
203     if (mutex == NULL) {
204         mutex = &globalMutex;
205     }
206     int sysErr = pthread_mutex_lock(&mutex->fMutex);
207     (void)sysErr;   // Suppress unused variable warnings.
208     U_ASSERT(sysErr == 0);
209 }
210 
211 
212 U_CAPI void  U_EXPORT2
umtx_unlock(UMutex * mutex)213 umtx_unlock(UMutex* mutex)
214 {
215     if (mutex == NULL) {
216         mutex = &globalMutex;
217     }
218     int sysErr = pthread_mutex_unlock(&mutex->fMutex);
219     (void)sysErr;   // Suppress unused variable warnings.
220     U_ASSERT(sysErr == 0);
221 }
222 
223 
224 U_CAPI void U_EXPORT2
umtx_condWait(UConditionVar * cond,UMutex * mutex)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
umtx_condBroadcast(UConditionVar * cond)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
umtx_condSignal(UConditionVar * cond)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 
250 U_NAMESPACE_BEGIN
251 
252 static pthread_mutex_t initMutex = PTHREAD_MUTEX_INITIALIZER;
253 static pthread_cond_t initCondition = PTHREAD_COND_INITIALIZER;
254 
255 
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
umtx_initImplPreInit(UInitOnce & uio)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;
281     }
282 }
283 
284 
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
umtx_initImplPostInit(UInitOnce & uio)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);
298 }
299 
300 U_NAMESPACE_END
301 
302 // End of POSIX specific umutex implementation.
303 
304 #else  // Platform #define chain.
305 
306 #error Unknown Platform
307 
308 #endif  // Platform #define chain.
309 
310 
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 //--------------------------------------------------------------------------------
320 
321 #if defined U_NO_PLATFORM_ATOMICS
322 static UMutex   gIncDecMutex = U_MUTEX_INITIALIZER;
323 
324 U_NAMESPACE_BEGIN
325 
326 U_COMMON_API int32_t U_EXPORT2
umtx_atomic_inc(u_atomic_int32_t * p)327 umtx_atomic_inc(u_atomic_int32_t *p)  {
328     int32_t retVal;
329     umtx_lock(&gIncDecMutex);
330     retVal = ++(*p);
331     umtx_unlock(&gIncDecMutex);
332     return retVal;
333 }
334 
335 
336 U_COMMON_API int32_t U_EXPORT2
umtx_atomic_dec(u_atomic_int32_t * p)337 umtx_atomic_dec(u_atomic_int32_t *p) {
338     int32_t retVal;
339     umtx_lock(&gIncDecMutex);
340     retVal = --(*p);
341     umtx_unlock(&gIncDecMutex);
342     return retVal;
343 }
344 
345 U_COMMON_API int32_t U_EXPORT2
umtx_loadAcquire(u_atomic_int32_t & var)346 umtx_loadAcquire(u_atomic_int32_t &var) {
347     umtx_lock(&gIncDecMutex);
348     int32_t val = var;
349     umtx_unlock(&gIncDecMutex);
350     return val;
351 }
352 
353 U_COMMON_API void U_EXPORT2
umtx_storeRelease(u_atomic_int32_t & var,int32_t val)354 umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
355     umtx_lock(&gIncDecMutex);
356     var = val;
357     umtx_unlock(&gIncDecMutex);
358 }
359 
360 U_NAMESPACE_END
361 #endif
362 
363 //--------------------------------------------------------------------------
364 //
365 //  Deprecated functions for setting user mutexes.
366 //
367 //--------------------------------------------------------------------------
368 
369 U_DEPRECATED void U_EXPORT2
u_setMutexFunctions(const void *,UMtxInitFn *,UMtxFn *,UMtxFn *,UMtxFn *,UErrorCode * status)370 u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *,
371                     UMtxFn *,  UMtxFn *, UErrorCode *status) {
372     if (U_SUCCESS(*status)) {
373         *status = U_UNSUPPORTED_ERROR;
374     }
375     return;
376 }
377 
378 
379 
380 U_DEPRECATED void U_EXPORT2
u_setAtomicIncDecFunctions(const void *,UMtxAtomicFn *,UMtxAtomicFn *,UErrorCode * status)381 u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *,
382                            UErrorCode *status) {
383     if (U_SUCCESS(*status)) {
384         *status = U_UNSUPPORTED_ERROR;
385     }
386     return;
387 }
388