1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 **********************************************************************
5 * Copyright (C) 1997-2015, International Business Machines
6 * Corporation and others. All Rights Reserved.
7 **********************************************************************
8 *
9 * File UMUTEX.H
10 *
11 * Modification History:
12 *
13 * Date Name Description
14 * 04/02/97 aliu Creation.
15 * 04/07/99 srl rewrite - C interface, multiple mutices
16 * 05/13/99 stephen Changed to umutex (from cmutex)
17 ******************************************************************************
18 */
19
20 #ifndef UMUTEX_H
21 #define UMUTEX_H
22
23 #include "unicode/utypes.h"
24 #include "unicode/uclean.h"
25 #include "putilimp.h"
26
27
28
29 // Forward Declarations. UMutex is not in the ICU namespace (yet) because
30 // there are some remaining references from plain C.
31 struct UMutex;
32 struct UConditionVar;
33
34 U_NAMESPACE_BEGIN
35 struct UInitOnce;
36 U_NAMESPACE_END
37
38 // Stringify macros, to allow #include of user supplied atomic & mutex files.
39 #define U_MUTEX_STR(s) #s
40 #define U_MUTEX_XSTR(s) U_MUTEX_STR(s)
41
42 /****************************************************************************
43 *
44 * Low Level Atomic Operations.
45 * Compiler dependent. Not operating system dependent.
46 *
47 ****************************************************************************/
48 #if defined (U_USER_ATOMICS_H)
49 #include U_MUTEX_XSTR(U_USER_ATOMICS_H)
50
51 #elif U_HAVE_STD_ATOMICS
52
53 // C++11 atomics are available.
54
55 #include <atomic>
56
57 // Export an explicit template instantiation of std::atomic<int32_t>.
58 // When building DLLs for Windows this is required as it is used as a data member of the exported SharedObject class.
59 // See digitlst.h, pluralaffix.h, datefmt.h, and others for similar examples.
60 #if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN && !defined(U_IN_DOXYGEN)
61 #if defined(__clang__)
62 // Suppress the warning that the explicit instantiation after explicit specialization has no effect.
63 #pragma clang diagnostic push
64 #pragma clang diagnostic ignored "-Winstantiation-after-specialization"
65 #endif
66 template struct U_COMMON_API std::atomic<int32_t>;
67 #if defined(__clang__)
68 #pragma clang diagnostic pop
69 #endif
70 #endif
71
72 U_NAMESPACE_BEGIN
73
74 typedef std::atomic<int32_t> u_atomic_int32_t;
75 #define ATOMIC_INT32_T_INITIALIZER(val) ATOMIC_VAR_INIT(val)
76
77 inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
78 return var.load(std::memory_order_acquire);
79 }
80
81 inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
82 var.store(val, std::memory_order_release);
83 }
84
85 inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
86 return var->fetch_add(1) + 1;
87 }
88
89 inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
90 return var->fetch_sub(1) - 1;
91 }
92 U_NAMESPACE_END
93
94 #elif U_PLATFORM_HAS_WIN32_API
95
96 // MSVC compiler. Reads and writes of volatile variables have
97 // acquire and release memory semantics, respectively.
98 // This is a Microsoft extension, not standard C++ behavior.
99 //
100 // Update: can't use this because of MinGW, built with gcc.
101 // Original plan was to use gcc atomics for MinGW, but they
102 // aren't supported, so we fold MinGW into this path.
103
104 #ifndef WIN32_LEAN_AND_MEAN
105 # define WIN32_LEAN_AND_MEAN
106 #endif
107 # define VC_EXTRALEAN
108 # define NOUSER
109 # define NOSERVICE
110 # define NOIME
111 # define NOMCX
112 # ifndef NOMINMAX
113 # define NOMINMAX
114 # endif
115 # include <windows.h>
116
117 U_NAMESPACE_BEGIN
118 typedef volatile LONG u_atomic_int32_t;
119 #define ATOMIC_INT32_T_INITIALIZER(val) val
120
121 inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
122 return InterlockedCompareExchange(&var, 0, 0);
123 }
124
125 inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
126 InterlockedExchange(&var, val);
127 }
128
129
130 inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
131 return InterlockedIncrement(var);
132 }
133
134 inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
135 return InterlockedDecrement(var);
136 }
137 U_NAMESPACE_END
138
139
140 #elif U_HAVE_CLANG_ATOMICS
141 /*
142 * Clang __c11 atomic built-ins
143 */
144
145 U_NAMESPACE_BEGIN
146 typedef _Atomic(int32_t) u_atomic_int32_t;
147 #define ATOMIC_INT32_T_INITIALIZER(val) val
148
149 inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
150 return __c11_atomic_load(&var, __ATOMIC_ACQUIRE);
151 }
152
153 inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
154 return __c11_atomic_store(&var, val, __ATOMIC_RELEASE);
155 }
156
157 inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
158 return __c11_atomic_fetch_add(var, 1, __ATOMIC_SEQ_CST) + 1;
159 }
160
161 inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
162 return __c11_atomic_fetch_sub(var, 1, __ATOMIC_SEQ_CST) - 1;
163 }
164 U_NAMESPACE_END
165
166
167 #elif U_HAVE_GCC_ATOMICS
168 /*
169 * gcc atomic ops. These are available on several other compilers as well.
170 */
171
172 U_NAMESPACE_BEGIN
173 typedef int32_t u_atomic_int32_t;
174 #define ATOMIC_INT32_T_INITIALIZER(val) val
175
176 inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
177 int32_t val = var;
178 __sync_synchronize();
179 return val;
180 }
181
182 inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
183 __sync_synchronize();
184 var = val;
185 }
186
187 inline int32_t umtx_atomic_inc(u_atomic_int32_t *p) {
188 return __sync_add_and_fetch(p, 1);
189 }
190
191 inline int32_t umtx_atomic_dec(u_atomic_int32_t *p) {
192 return __sync_sub_and_fetch(p, 1);
193 }
194 U_NAMESPACE_END
195
196 #else
197
198 /*
199 * Unknown Platform. Use out-of-line functions, which in turn use mutexes.
200 * Slow but correct.
201 */
202
203 #define U_NO_PLATFORM_ATOMICS
204
205 U_NAMESPACE_BEGIN
206 typedef int32_t u_atomic_int32_t;
207 #define ATOMIC_INT32_T_INITIALIZER(val) val
208
209 U_COMMON_API int32_t U_EXPORT2
210 umtx_loadAcquire(u_atomic_int32_t &var);
211
212 U_COMMON_API void U_EXPORT2
213 umtx_storeRelease(u_atomic_int32_t &var, int32_t val);
214
215 U_COMMON_API int32_t U_EXPORT2
216 umtx_atomic_inc(u_atomic_int32_t *p);
217
218 U_COMMON_API int32_t U_EXPORT2
219 umtx_atomic_dec(u_atomic_int32_t *p);
220
221 U_NAMESPACE_END
222
223 #endif /* Low Level Atomic Ops Platform Chain */
224
225
226
227 /*************************************************************************************************
228 *
229 * UInitOnce Definitions.
230 * These are platform neutral.
231 *
232 *************************************************************************************************/
233
234 U_NAMESPACE_BEGIN
235
236 struct UInitOnce {
237 u_atomic_int32_t fState;
238 UErrorCode fErrCode;
resetUInitOnce239 void reset() {fState = 0;};
isResetUInitOnce240 UBool isReset() {return umtx_loadAcquire(fState) == 0;};
241 // Note: isReset() is used by service registration code.
242 // Thread safety of this usage needs review.
243 };
244
245 #define U_INITONCE_INITIALIZER {ATOMIC_INT32_T_INITIALIZER(0), U_ZERO_ERROR}
246
247
248 U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &);
249 U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &);
250
umtx_initOnce(UInitOnce & uio,T * obj,void (U_CALLCONV T::* fp)())251 template<class T> void umtx_initOnce(UInitOnce &uio, T *obj, void (U_CALLCONV T::*fp)()) {
252 if (umtx_loadAcquire(uio.fState) == 2) {
253 return;
254 }
255 if (umtx_initImplPreInit(uio)) {
256 (obj->*fp)();
257 umtx_initImplPostInit(uio);
258 }
259 }
260
261
262 // umtx_initOnce variant for plain functions, or static class functions.
263 // No context parameter.
umtx_initOnce(UInitOnce & uio,void (U_CALLCONV * fp)())264 inline void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)()) {
265 if (umtx_loadAcquire(uio.fState) == 2) {
266 return;
267 }
268 if (umtx_initImplPreInit(uio)) {
269 (*fp)();
270 umtx_initImplPostInit(uio);
271 }
272 }
273
274 // umtx_initOnce variant for plain functions, or static class functions.
275 // With ErrorCode, No context parameter.
umtx_initOnce(UInitOnce & uio,void (U_CALLCONV * fp)(UErrorCode &),UErrorCode & errCode)276 inline void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(UErrorCode &), UErrorCode &errCode) {
277 if (U_FAILURE(errCode)) {
278 return;
279 }
280 if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) {
281 // We run the initialization.
282 (*fp)(errCode);
283 uio.fErrCode = errCode;
284 umtx_initImplPostInit(uio);
285 } else {
286 // Someone else already ran the initialization.
287 if (U_FAILURE(uio.fErrCode)) {
288 errCode = uio.fErrCode;
289 }
290 }
291 }
292
293 // umtx_initOnce variant for plain functions, or static class functions,
294 // with a context parameter.
umtx_initOnce(UInitOnce & uio,void (U_CALLCONV * fp)(T),T context)295 template<class T> void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(T), T context) {
296 if (umtx_loadAcquire(uio.fState) == 2) {
297 return;
298 }
299 if (umtx_initImplPreInit(uio)) {
300 (*fp)(context);
301 umtx_initImplPostInit(uio);
302 }
303 }
304
305 // umtx_initOnce variant for plain functions, or static class functions,
306 // with a context parameter and an error code.
umtx_initOnce(UInitOnce & uio,void (U_CALLCONV * fp)(T,UErrorCode &),T context,UErrorCode & errCode)307 template<class T> void umtx_initOnce(UInitOnce &uio, void (U_CALLCONV *fp)(T, UErrorCode &), T context, UErrorCode &errCode) {
308 if (U_FAILURE(errCode)) {
309 return;
310 }
311 if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) {
312 // We run the initialization.
313 (*fp)(context, errCode);
314 uio.fErrCode = errCode;
315 umtx_initImplPostInit(uio);
316 } else {
317 // Someone else already ran the initialization.
318 if (U_FAILURE(uio.fErrCode)) {
319 errCode = uio.fErrCode;
320 }
321 }
322 }
323
324 U_NAMESPACE_END
325
326
327
328 /*************************************************************************************************
329 *
330 * Mutex Definitions. Platform Dependent, #if platform chain follows.
331 * TODO: Add a C++11 version.
332 * Need to convert all mutex using files to C++ first.
333 *
334 *************************************************************************************************/
335
336 #if defined(U_USER_MUTEX_H)
337 // #include "U_USER_MUTEX_H"
338 #include U_MUTEX_XSTR(U_USER_MUTEX_H)
339
340 #elif U_PLATFORM_USES_ONLY_WIN32_API
341
342 /* For CRITICAL_SECTION */
343
344 /*
345 * Note: there is an earlier include of windows.h in this file, but it is in
346 * different conditionals.
347 * This one is needed if we are using C++11 for atomic ops, but
348 * win32 APIs for Critical Sections.
349 */
350
351 #ifndef WIN32_LEAN_AND_MEAN
352 # define WIN32_LEAN_AND_MEAN
353 #endif
354 # define VC_EXTRALEAN
355 # define NOUSER
356 # define NOSERVICE
357 # define NOIME
358 # define NOMCX
359 # ifndef NOMINMAX
360 # define NOMINMAX
361 # endif
362 # include <windows.h>
363
364
365 typedef struct UMutex {
366 icu::UInitOnce fInitOnce;
367 CRITICAL_SECTION fCS;
368 } UMutex;
369
370 /* Initializer for a static UMUTEX. Deliberately contains no value for the
371 * CRITICAL_SECTION.
372 */
373 #define U_MUTEX_INITIALIZER {U_INITONCE_INITIALIZER}
374
375 struct UConditionVar {
376 HANDLE fEntryGate;
377 HANDLE fExitGate;
378 int32_t fWaitCount;
379 };
380
381 #define U_CONDITION_INITIALIZER {NULL, NULL, 0}
382
383
384
385 #elif U_PLATFORM_IMPLEMENTS_POSIX
386
387 /*
388 * POSIX platform
389 */
390
391 #include <pthread.h>
392
393 struct UMutex {
394 pthread_mutex_t fMutex;
395 };
396 typedef struct UMutex UMutex;
397 #define U_MUTEX_INITIALIZER {PTHREAD_MUTEX_INITIALIZER}
398
399 struct UConditionVar {
400 pthread_cond_t fCondition;
401 };
402 #define U_CONDITION_INITIALIZER {PTHREAD_COND_INITIALIZER}
403
404 #else
405
406 /*
407 * Unknown platform type.
408 * This is an error condition. ICU requires mutexes.
409 */
410
411 #error Unknown Platform.
412
413 #endif
414
415
416
417 /**************************************************************************************
418 *
419 * Mutex Implementation function declarations.
420 * Declarations are platform neutral.
421 * Implementations, in umutex.cpp, are platform specific.
422 *
423 ************************************************************************************/
424
425 /* Lock a mutex.
426 * @param mutex The given mutex to be locked. Pass NULL to specify
427 * the global ICU mutex. Recursive locks are an error
428 * and may cause a deadlock on some platforms.
429 */
430 U_INTERNAL void U_EXPORT2 umtx_lock(UMutex* mutex);
431
432 /* Unlock a mutex.
433 * @param mutex The given mutex to be unlocked. Pass NULL to specify
434 * the global ICU mutex.
435 */
436 U_INTERNAL void U_EXPORT2 umtx_unlock (UMutex* mutex);
437
438 /*
439 * Wait on a condition variable.
440 * The calling thread will unlock the mutex and wait on the condition variable.
441 * The mutex must be locked by the calling thread when invoking this function.
442 *
443 * @param cond the condition variable to wait on.
444 * @param mutex the associated mutex.
445 */
446
447 U_INTERNAL void U_EXPORT2 umtx_condWait(UConditionVar *cond, UMutex *mutex);
448
449
450 /*
451 * Broadcast wakeup of all threads waiting on a Condition.
452 * The associated mutex must be locked by the calling thread when calling
453 * this function; this is a temporary ICU restriction.
454 *
455 * @param cond the condition variable.
456 */
457 U_INTERNAL void U_EXPORT2 umtx_condBroadcast(UConditionVar *cond);
458
459 /*
460 * Signal a condition variable, waking up one waiting thread.
461 * CAUTION: Do not use. Place holder only. Not implemented for Windows.
462 */
463 U_INTERNAL void U_EXPORT2 umtx_condSignal(UConditionVar *cond);
464
465 #endif /* UMUTEX_H */
466 /*eof*/
467