1/*
2******************************************************************************
3*
4*   Copyright (C) 1997-2013, 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#include "ucln_cmn.h"
27
28
29// The ICU global mutex. Used when ICU implementation code passes NULL for the mutex pointer.
30static UMutex   globalMutex = U_MUTEX_INITIALIZER;
31
32/*
33 * ICU Mutex wrappers.  Wrap operating system mutexes, giving the rest of ICU a
34 * platform independent set of mutex operations.  For internal ICU use only.
35 */
36
37#if defined(U_USER_MUTEX_CPP)
38// Build time user mutex hook: #include "U_USER_MUTEX_CPP"
39#include U_MUTEX_XSTR(U_USER_MUTEX_CPP)
40
41#elif U_PLATFORM_HAS_WIN32_API
42
43//-------------------------------------------------------------------------------------------
44//
45//    Windows Specific Definitions
46//
47//        Note: Cygwin (and possibly others) have both WIN32 and POSIX.
48//              Prefer Win32 in these cases.  (Win32 comes ahead in the #if chain)
49//
50//-------------------------------------------------------------------------------------------
51
52#if defined U_NO_PLATFORM_ATOMICS
53#error ICU on Win32 requires support for low level atomic operations.
54// Visual Studio, gcc, clang are OK. Shouldn't get here.
55#endif
56
57
58// This function is called when a test of a UInitOnce::fState reveals that
59//   initialization has not completed, that we either need to call the
60//   function on this thread, or wait for some other thread to complete.
61//
62// The actual call to the init function is made inline by template code
63//   that knows the C++ types involved. This function returns TRUE if
64//   the caller needs to call the Init function.
65//
66
67U_NAMESPACE_BEGIN
68
69U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &uio) {
70    for (;;) {
71        int32_t previousState = InterlockedCompareExchange(
72#if (U_PLATFORM == U_PF_MINGW) || (U_PLATFORM == U_PF_CYGWIN)
73           (LONG volatile *) // this is the type given in the API doc for this function.
74#endif
75            &uio.fState,  //  Destination
76            1,            //  Exchange Value
77            0);           //  Compare value
78
79        if (previousState == 0) {
80            return true;   // Caller will next call the init function.
81                           // Current state == 1.
82        } else if (previousState == 2) {
83            // Another thread already completed the initialization.
84            //   We can simply return FALSE, indicating no
85            //   further action is needed by the caller.
86            return FALSE;
87        } else {
88            // Another thread is currently running the initialization.
89            // Wait until it completes.
90            do {
91                Sleep(1);
92                previousState = umtx_loadAcquire(uio.fState);
93            } while (previousState == 1);
94        }
95    }
96}
97
98// This function is called by the thread that ran an initialization function,
99// just after completing the function.
100//
101//   success: True:  the inialization succeeded. No further calls to the init
102//                   function will be made.
103//            False: the initializtion failed. The next call to umtx_initOnce()
104//                   will retry the initialization.
105
106U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &uio) {
107    umtx_storeRelease(uio.fState, 2);
108}
109
110U_NAMESPACE_END
111
112static void winMutexInit(CRITICAL_SECTION *cs) {
113    InitializeCriticalSection(cs);
114    return;
115}
116
117U_CAPI void  U_EXPORT2
118umtx_lock(UMutex *mutex) {
119    if (mutex == NULL) {
120        mutex = &globalMutex;
121    }
122    CRITICAL_SECTION *cs = &mutex->fCS;
123    umtx_initOnce(mutex->fInitOnce, winMutexInit, cs);
124    EnterCriticalSection(cs);
125}
126
127U_CAPI void  U_EXPORT2
128umtx_unlock(UMutex* mutex)
129{
130    if (mutex == NULL) {
131        mutex = &globalMutex;
132    }
133    LeaveCriticalSection(&mutex->fCS);
134}
135
136#elif U_PLATFORM_IMPLEMENTS_POSIX
137
138//-------------------------------------------------------------------------------------------
139//
140//  POSIX specific definitions
141//
142//-------------------------------------------------------------------------------------------
143
144# include <pthread.h>
145
146// Each UMutex consists of a pthread_mutex_t.
147// All are statically initialized and ready for use.
148// There is no runtime mutex initialization code needed.
149
150U_CAPI void  U_EXPORT2
151umtx_lock(UMutex *mutex) {
152    if (mutex == NULL) {
153        mutex = &globalMutex;
154    }
155    int sysErr = pthread_mutex_lock(&mutex->fMutex);
156    (void)sysErr;   // Suppress unused variable warnings.
157    U_ASSERT(sysErr == 0);
158}
159
160
161U_CAPI void  U_EXPORT2
162umtx_unlock(UMutex* mutex)
163{
164    if (mutex == NULL) {
165        mutex = &globalMutex;
166    }
167    int sysErr = pthread_mutex_unlock(&mutex->fMutex);
168    (void)sysErr;   // Suppress unused variable warnings.
169    U_ASSERT(sysErr == 0);
170}
171
172U_NAMESPACE_BEGIN
173
174static pthread_mutex_t initMutex = PTHREAD_MUTEX_INITIALIZER;
175static pthread_cond_t initCondition = PTHREAD_COND_INITIALIZER;
176
177
178// This function is called when a test of a UInitOnce::fState reveals that
179//   initialization has not completed, that we either need to call the
180//   function on this thread, or wait for some other thread to complete.
181//
182// The actual call to the init function is made inline by template code
183//   that knows the C++ types involved. This function returns TRUE if
184//   the caller needs to call the Init function.
185//
186U_COMMON_API UBool U_EXPORT2
187umtx_initImplPreInit(UInitOnce &uio) {
188    pthread_mutex_lock(&initMutex);
189    int32_t state = uio.fState;
190    if (state == 0) {
191        umtx_storeRelease(uio.fState, 1);
192        pthread_mutex_unlock(&initMutex);
193        return TRUE;   // Caller will next call the init function.
194    } else {
195        while (uio.fState == 1) {
196            // Another thread is currently running the initialization.
197            // Wait until it completes.
198            pthread_cond_wait(&initCondition, &initMutex);
199        }
200        pthread_mutex_unlock(&initMutex);
201        U_ASSERT(uio.fState == 2);
202        return FALSE;
203    }
204}
205
206
207
208// This function is called by the thread that ran an initialization function,
209// just after completing the function.
210//   Some threads may be waiting on the condition, requiring the broadcast wakeup.
211//   Some threads may be racing to test the fState variable outside of the mutex,
212//   requiring the use of store/release when changing its value.
213
214U_COMMON_API void U_EXPORT2
215umtx_initImplPostInit(UInitOnce &uio) {
216    pthread_mutex_lock(&initMutex);
217    umtx_storeRelease(uio.fState, 2);
218    pthread_cond_broadcast(&initCondition);
219    pthread_mutex_unlock(&initMutex);
220}
221
222U_NAMESPACE_END
223
224// End of POSIX specific umutex implementation.
225
226#else  // Platform #define chain.
227
228#error Unknown Platform
229
230#endif  // Platform #define chain.
231
232
233//-------------------------------------------------------------------------------
234//
235//   Atomic Operations, out-of-line versions.
236//                      These are conditional, only defined if better versions
237//                      were not available for the platform.
238//
239//                      These versions are platform neutral.
240//
241//--------------------------------------------------------------------------------
242
243#if defined U_NO_PLATFORM_ATOMICS
244static UMutex   gIncDecMutex = U_MUTEX_INITIALIZER;
245
246U_NAMESPACE_BEGIN
247
248U_COMMON_API int32_t U_EXPORT2
249umtx_atomic_inc(u_atomic_int32_t *p)  {
250    int32_t retVal;
251    umtx_lock(&gIncDecMutex);
252    retVal = ++(*p);
253    umtx_unlock(&gIncDecMutex);
254    return retVal;
255}
256
257
258U_COMMON_API int32_t U_EXPORT2
259umtx_atomic_dec(u_atomic_int32_t *p) {
260    int32_t retVal;
261    umtx_lock(&gIncDecMutex);
262    retVal = --(*p);
263    umtx_unlock(&gIncDecMutex);
264    return retVal;
265}
266
267U_COMMON_API int32_t U_EXPORT2
268umtx_loadAcquire(u_atomic_int32_t &var) {
269    int32_t val = var;
270    umtx_lock(&gIncDecMutex);
271    umtx_unlock(&gIncDecMutex);
272    return val;
273}
274
275U_COMMON_API void U_EXPORT2
276umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
277    umtx_lock(&gIncDecMutex);
278    umtx_unlock(&gIncDecMutex);
279    var = val;
280}
281
282U_NAMESPACE_END
283#endif
284
285//--------------------------------------------------------------------------
286//
287//  Deprecated functions for setting user mutexes.
288//
289//--------------------------------------------------------------------------
290
291U_DEPRECATED void U_EXPORT2
292u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *,
293                    UMtxFn *,  UMtxFn *, UErrorCode *status) {
294    if (U_SUCCESS(*status)) {
295        *status = U_UNSUPPORTED_ERROR;
296    }
297    return;
298}
299
300
301
302U_DEPRECATED void U_EXPORT2
303u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *,
304                           UErrorCode *status) {
305    if (U_SUCCESS(*status)) {
306        *status = U_UNSUPPORTED_ERROR;
307    }
308    return;
309}
310