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