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