]> git.saurik.com Git - apple/icu.git/blame - icuSources/common/umutex.cpp
ICU-62108.0.1.tar.gz
[apple/icu.git] / icuSources / common / umutex.cpp
CommitLineData
f3c0d7a5
A
1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
51004dcb
A
3/*
4******************************************************************************
5*
f3c0d7a5 6* Copyright (C) 1997-2016, International Business Machines
51004dcb
A
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
57a6839d
A
23#include "umutex.h"
24
51004dcb
A
25#include "unicode/utypes.h"
26#include "uassert.h"
57a6839d 27#include "cmemory.h"
51004dcb 28
57a6839d
A
29
30// The ICU global mutex. Used when ICU implementation code passes NULL for the mutex pointer.
31static UMutex globalMutex = U_MUTEX_INITIALIZER;
32
51004dcb
A
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
57a6839d
A
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)
51004dcb 41
f3c0d7a5 42#elif U_PLATFORM_USES_ONLY_WIN32_API
51004dcb 43
57a6839d
A
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.
51004dcb
A
47#endif
48
51004dcb 49
57a6839d
A
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.
51004dcb 53//
57a6839d
A
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.
51004dcb 57//
51004dcb 58
57a6839d 59U_NAMESPACE_BEGIN
51004dcb 60
57a6839d
A
61U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &uio) {
62 for (;;) {
63 int32_t previousState = InterlockedCompareExchange(
f3c0d7a5
A
64 (LONG volatile *) // this is the type given in the API doc for this function.
65 &uio.fState, // Destination
57a6839d
A
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);
51004dcb 84 }
51004dcb 85 }
51004dcb
A
86}
87
57a6839d
A
88// This function is called by the thread that ran an initialization function,
89// just after completing the function.
51004dcb 90
57a6839d
A
91U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &uio) {
92 umtx_storeRelease(uio.fState, 2);
51004dcb 93}
51004dcb 94
57a6839d 95U_NAMESPACE_END
51004dcb 96
57a6839d
A
97static void winMutexInit(CRITICAL_SECTION *cs) {
98 InitializeCriticalSection(cs);
99 return;
100}
51004dcb
A
101
102U_CAPI void U_EXPORT2
103umtx_lock(UMutex *mutex) {
104 if (mutex == NULL) {
105 mutex = &globalMutex;
106 }
57a6839d
A
107 CRITICAL_SECTION *cs = &mutex->fCS;
108 umtx_initOnce(mutex->fInitOnce, winMutexInit, cs);
109 EnterCriticalSection(cs);
51004dcb
A
110}
111
51004dcb
A
112U_CAPI void U_EXPORT2
113umtx_unlock(UMutex* mutex)
114{
115 if (mutex == NULL) {
116 mutex = &globalMutex;
117 }
57a6839d 118 LeaveCriticalSection(&mutex->fCS);
51004dcb
A
119}
120
b331163b
A
121
122U_CAPI void U_EXPORT2
123umtx_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
134U_CAPI void U_EXPORT2
0f5d89e8 135umtx_condSignal(UConditionVar * /* condition */) {
b331163b
A
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
143U_CAPI void U_EXPORT2
144umtx_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
57a6839d
A
178#elif U_PLATFORM_IMPLEMENTS_POSIX
179
180//-------------------------------------------------------------------------------------------
51004dcb 181//
57a6839d 182// POSIX specific definitions
51004dcb 183//
57a6839d
A
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.
51004dcb 191
51004dcb
A
192U_CAPI void U_EXPORT2
193umtx_lock(UMutex *mutex) {
194 if (mutex == NULL) {
195 mutex = &globalMutex;
196 }
57a6839d
A
197 int sysErr = pthread_mutex_lock(&mutex->fMutex);
198 (void)sysErr; // Suppress unused variable warnings.
199 U_ASSERT(sysErr == 0);
51004dcb
A
200}
201
57a6839d 202
51004dcb
A
203U_CAPI void U_EXPORT2
204umtx_unlock(UMutex* mutex)
205{
206 if (mutex == NULL) {
207 mutex = &globalMutex;
208 }
57a6839d
A
209 int sysErr = pthread_mutex_unlock(&mutex->fMutex);
210 (void)sysErr; // Suppress unused variable warnings.
211 U_ASSERT(sysErr == 0);
51004dcb
A
212}
213
b331163b
A
214
215U_CAPI void U_EXPORT2
216umtx_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
225U_CAPI void U_EXPORT2
226umtx_condBroadcast(UConditionVar *cond) {
227 int sysErr = pthread_cond_broadcast(&cond->fCondition);
228 (void)sysErr;
229 U_ASSERT(sysErr == 0);
230}
231
232U_CAPI void U_EXPORT2
233umtx_condSignal(UConditionVar *cond) {
234 int sysErr = pthread_cond_signal(&cond->fCondition);
235 (void)sysErr;
236 U_ASSERT(sysErr == 0);
237}
238
239
240
57a6839d 241U_NAMESPACE_BEGIN
51004dcb 242
57a6839d
A
243static pthread_mutex_t initMutex = PTHREAD_MUTEX_INITIALIZER;
244static pthread_cond_t initCondition = PTHREAD_COND_INITIALIZER;
51004dcb 245
51004dcb 246
57a6839d
A
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//
255U_COMMON_API UBool U_EXPORT2
256umtx_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;
51004dcb 272 }
57a6839d 273}
51004dcb 274
51004dcb 275
57a6839d
A
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
283U_COMMON_API void U_EXPORT2
284umtx_initImplPostInit(UInitOnce &uio) {
285 pthread_mutex_lock(&initMutex);
286 umtx_storeRelease(uio.fState, 2);
287 pthread_cond_broadcast(&initCondition);
288 pthread_mutex_unlock(&initMutex);
51004dcb
A
289}
290
57a6839d 291U_NAMESPACE_END
51004dcb 292
57a6839d 293// End of POSIX specific umutex implementation.
51004dcb 294
57a6839d 295#else // Platform #define chain.
51004dcb 296
57a6839d 297#error Unknown Platform
51004dcb 298
57a6839d 299#endif // Platform #define chain.
51004dcb 300
51004dcb 301
57a6839d
A
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//--------------------------------------------------------------------------------
51004dcb 311
57a6839d 312#if defined U_NO_PLATFORM_ATOMICS
51004dcb 313static UMutex gIncDecMutex = U_MUTEX_INITIALIZER;
51004dcb 314
57a6839d
A
315U_NAMESPACE_BEGIN
316
317U_COMMON_API int32_t U_EXPORT2
318umtx_atomic_inc(u_atomic_int32_t *p) {
51004dcb 319 int32_t retVal;
57a6839d
A
320 umtx_lock(&gIncDecMutex);
321 retVal = ++(*p);
322 umtx_unlock(&gIncDecMutex);
51004dcb
A
323 return retVal;
324}
325
57a6839d
A
326
327U_COMMON_API int32_t U_EXPORT2
328umtx_atomic_dec(u_atomic_int32_t *p) {
51004dcb 329 int32_t retVal;
57a6839d
A
330 umtx_lock(&gIncDecMutex);
331 retVal = --(*p);
332 umtx_unlock(&gIncDecMutex);
51004dcb
A
333 return retVal;
334}
335
57a6839d
A
336U_COMMON_API int32_t U_EXPORT2
337umtx_loadAcquire(u_atomic_int32_t &var) {
57a6839d 338 umtx_lock(&gIncDecMutex);
2ca993e8 339 int32_t val = var;
57a6839d
A
340 umtx_unlock(&gIncDecMutex);
341 return val;
342}
343
344U_COMMON_API void U_EXPORT2
345umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
346 umtx_lock(&gIncDecMutex);
57a6839d 347 var = val;
2ca993e8 348 umtx_unlock(&gIncDecMutex);
57a6839d 349}
51004dcb 350
57a6839d
A
351U_NAMESPACE_END
352#endif
51004dcb 353
57a6839d
A
354//--------------------------------------------------------------------------
355//
356// Deprecated functions for setting user mutexes.
357//
358//--------------------------------------------------------------------------
51004dcb 359
57a6839d
A
360U_DEPRECATED void U_EXPORT2
361u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *,
362 UMtxFn *, UMtxFn *, UErrorCode *status) {
363 if (U_SUCCESS(*status)) {
364 *status = U_UNSUPPORTED_ERROR;
51004dcb 365 }
57a6839d 366 return;
51004dcb
A
367}
368
369
57a6839d
A
370
371U_DEPRECATED void U_EXPORT2
372u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *,
373 UErrorCode *status) {
374 if (U_SUCCESS(*status)) {
375 *status = U_UNSUPPORTED_ERROR;
376 }
377 return;
51004dcb 378}