]>
Commit | Line | Data |
---|---|---|
51004dcb A |
1 | /* |
2 | ****************************************************************************** | |
3 | * | |
57a6839d | 4 | * Copyright (C) 1997-2013, International Business Machines |
51004dcb A |
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 | ||
57a6839d A |
21 | #include "umutex.h" |
22 | ||
51004dcb A |
23 | #include "unicode/utypes.h" |
24 | #include "uassert.h" | |
57a6839d | 25 | #include "cmemory.h" |
51004dcb A |
26 | #include "ucln_cmn.h" |
27 | ||
57a6839d A |
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 | ||
51004dcb A |
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 | ||
57a6839d A |
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) | |
51004dcb | 40 | |
57a6839d | 41 | #elif U_PLATFORM_HAS_WIN32_API |
51004dcb | 42 | |
57a6839d A |
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 | //------------------------------------------------------------------------------------------- | |
51004dcb | 51 | |
57a6839d A |
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. | |
51004dcb A |
55 | #endif |
56 | ||
51004dcb | 57 | |
57a6839d A |
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. | |
51004dcb | 61 | // |
57a6839d A |
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. | |
51004dcb | 65 | // |
51004dcb | 66 | |
57a6839d | 67 | U_NAMESPACE_BEGIN |
51004dcb | 68 | |
57a6839d A |
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); | |
51004dcb | 94 | } |
51004dcb | 95 | } |
51004dcb A |
96 | } |
97 | ||
57a6839d A |
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. | |
51004dcb | 105 | |
57a6839d A |
106 | U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &uio) { |
107 | umtx_storeRelease(uio.fState, 2); | |
51004dcb | 108 | } |
51004dcb | 109 | |
57a6839d | 110 | U_NAMESPACE_END |
51004dcb | 111 | |
57a6839d A |
112 | static void winMutexInit(CRITICAL_SECTION *cs) { |
113 | InitializeCriticalSection(cs); | |
114 | return; | |
115 | } | |
51004dcb A |
116 | |
117 | U_CAPI void U_EXPORT2 | |
118 | umtx_lock(UMutex *mutex) { | |
119 | if (mutex == NULL) { | |
120 | mutex = &globalMutex; | |
121 | } | |
57a6839d A |
122 | CRITICAL_SECTION *cs = &mutex->fCS; |
123 | umtx_initOnce(mutex->fInitOnce, winMutexInit, cs); | |
124 | EnterCriticalSection(cs); | |
51004dcb A |
125 | } |
126 | ||
51004dcb A |
127 | U_CAPI void U_EXPORT2 |
128 | umtx_unlock(UMutex* mutex) | |
129 | { | |
130 | if (mutex == NULL) { | |
131 | mutex = &globalMutex; | |
132 | } | |
57a6839d | 133 | LeaveCriticalSection(&mutex->fCS); |
51004dcb A |
134 | } |
135 | ||
57a6839d A |
136 | #elif U_PLATFORM_IMPLEMENTS_POSIX |
137 | ||
138 | //------------------------------------------------------------------------------------------- | |
51004dcb | 139 | // |
57a6839d | 140 | // POSIX specific definitions |
51004dcb | 141 | // |
57a6839d A |
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. | |
51004dcb | 149 | |
51004dcb A |
150 | U_CAPI void U_EXPORT2 |
151 | umtx_lock(UMutex *mutex) { | |
152 | if (mutex == NULL) { | |
153 | mutex = &globalMutex; | |
154 | } | |
57a6839d A |
155 | int sysErr = pthread_mutex_lock(&mutex->fMutex); |
156 | (void)sysErr; // Suppress unused variable warnings. | |
157 | U_ASSERT(sysErr == 0); | |
51004dcb A |
158 | } |
159 | ||
57a6839d | 160 | |
51004dcb A |
161 | U_CAPI void U_EXPORT2 |
162 | umtx_unlock(UMutex* mutex) | |
163 | { | |
164 | if (mutex == NULL) { | |
165 | mutex = &globalMutex; | |
166 | } | |
57a6839d A |
167 | int sysErr = pthread_mutex_unlock(&mutex->fMutex); |
168 | (void)sysErr; // Suppress unused variable warnings. | |
169 | U_ASSERT(sysErr == 0); | |
51004dcb A |
170 | } |
171 | ||
57a6839d | 172 | U_NAMESPACE_BEGIN |
51004dcb | 173 | |
57a6839d A |
174 | static pthread_mutex_t initMutex = PTHREAD_MUTEX_INITIALIZER; |
175 | static pthread_cond_t initCondition = PTHREAD_COND_INITIALIZER; | |
51004dcb | 176 | |
51004dcb | 177 | |
57a6839d A |
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; | |
51004dcb | 203 | } |
57a6839d | 204 | } |
51004dcb | 205 | |
51004dcb | 206 | |
57a6839d A |
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); | |
51004dcb A |
220 | } |
221 | ||
57a6839d | 222 | U_NAMESPACE_END |
51004dcb | 223 | |
57a6839d | 224 | // End of POSIX specific umutex implementation. |
51004dcb | 225 | |
57a6839d | 226 | #else // Platform #define chain. |
51004dcb | 227 | |
57a6839d | 228 | #error Unknown Platform |
51004dcb | 229 | |
57a6839d | 230 | #endif // Platform #define chain. |
51004dcb | 231 | |
51004dcb | 232 | |
57a6839d A |
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 | //-------------------------------------------------------------------------------- | |
51004dcb | 242 | |
57a6839d | 243 | #if defined U_NO_PLATFORM_ATOMICS |
51004dcb | 244 | static UMutex gIncDecMutex = U_MUTEX_INITIALIZER; |
51004dcb | 245 | |
57a6839d A |
246 | U_NAMESPACE_BEGIN |
247 | ||
248 | U_COMMON_API int32_t U_EXPORT2 | |
249 | umtx_atomic_inc(u_atomic_int32_t *p) { | |
51004dcb | 250 | int32_t retVal; |
57a6839d A |
251 | umtx_lock(&gIncDecMutex); |
252 | retVal = ++(*p); | |
253 | umtx_unlock(&gIncDecMutex); | |
51004dcb A |
254 | return retVal; |
255 | } | |
256 | ||
57a6839d A |
257 | |
258 | U_COMMON_API int32_t U_EXPORT2 | |
259 | umtx_atomic_dec(u_atomic_int32_t *p) { | |
51004dcb | 260 | int32_t retVal; |
57a6839d A |
261 | umtx_lock(&gIncDecMutex); |
262 | retVal = --(*p); | |
263 | umtx_unlock(&gIncDecMutex); | |
51004dcb A |
264 | return retVal; |
265 | } | |
266 | ||
57a6839d A |
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 | } | |
51004dcb | 281 | |
57a6839d A |
282 | U_NAMESPACE_END |
283 | #endif | |
51004dcb | 284 | |
57a6839d A |
285 | //-------------------------------------------------------------------------- |
286 | // | |
287 | // Deprecated functions for setting user mutexes. | |
288 | // | |
289 | //-------------------------------------------------------------------------- | |
51004dcb | 290 | |
57a6839d A |
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; | |
51004dcb | 296 | } |
57a6839d | 297 | return; |
51004dcb A |
298 | } |
299 | ||
300 | ||
57a6839d A |
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; | |
51004dcb | 309 | } |