]> git.saurik.com Git - apple/icu.git/blob - icuSources/common/umutex.cpp
ICU-531.48.tar.gz
[apple/icu.git] / icuSources / common / umutex.cpp
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.
30 static 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
67 U_NAMESPACE_BEGIN
68
69 U_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
106 U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &uio) {
107 umtx_storeRelease(uio.fState, 2);
108 }
109
110 U_NAMESPACE_END
111
112 static void winMutexInit(CRITICAL_SECTION *cs) {
113 InitializeCriticalSection(cs);
114 return;
115 }
116
117 U_CAPI void U_EXPORT2
118 umtx_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
127 U_CAPI void U_EXPORT2
128 umtx_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
150 U_CAPI void U_EXPORT2
151 umtx_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
161 U_CAPI void U_EXPORT2
162 umtx_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
172 U_NAMESPACE_BEGIN
173
174 static pthread_mutex_t initMutex = PTHREAD_MUTEX_INITIALIZER;
175 static 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 //
186 U_COMMON_API UBool U_EXPORT2
187 umtx_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
214 U_COMMON_API void U_EXPORT2
215 umtx_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
222 U_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
244 static UMutex gIncDecMutex = U_MUTEX_INITIALIZER;
245
246 U_NAMESPACE_BEGIN
247
248 U_COMMON_API int32_t U_EXPORT2
249 umtx_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
258 U_COMMON_API int32_t U_EXPORT2
259 umtx_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
267 U_COMMON_API int32_t U_EXPORT2
268 umtx_loadAcquire(u_atomic_int32_t &var) {
269 int32_t val = var;
270 umtx_lock(&gIncDecMutex);
271 umtx_unlock(&gIncDecMutex);
272 return val;
273 }
274
275 U_COMMON_API void U_EXPORT2
276 umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
277 umtx_lock(&gIncDecMutex);
278 umtx_unlock(&gIncDecMutex);
279 var = val;
280 }
281
282 U_NAMESPACE_END
283 #endif
284
285 //--------------------------------------------------------------------------
286 //
287 // Deprecated functions for setting user mutexes.
288 //
289 //--------------------------------------------------------------------------
290
291 U_DEPRECATED void U_EXPORT2
292 u_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
302 U_DEPRECATED void U_EXPORT2
303 u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *,
304 UErrorCode *status) {
305 if (U_SUCCESS(*status)) {
306 *status = U_UNSUPPORTED_ERROR;
307 }
308 return;
309 }