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.
31struct UMutex;
32struct UConditionVar;
33
34U_NAMESPACE_BEGIN
35struct UInitOnce;
36U_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
57U_NAMESPACE_BEGIN
58
59typedef std::atomic<int32_t> u_atomic_int32_t;
60#define ATOMIC_INT32_T_INITIALIZER(val) ATOMIC_VAR_INIT(val)
61
62inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
63    return var.load(std::memory_order_acquire);
64}
65
66inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
67    var.store(val, std::memory_order_release);
68}
69
70inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
71    return var->fetch_add(1) + 1;
72}
73
74inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
75    return var->fetch_sub(1) - 1;
76}
77U_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
100U_NAMESPACE_BEGIN
101typedef volatile LONG u_atomic_int32_t;
102#define ATOMIC_INT32_T_INITIALIZER(val) val
103
104inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
105    return InterlockedCompareExchange(&var, 0, 0);
106}
107
108inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
109    InterlockedExchange(&var, val);
110}
111
112
113inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
114    return InterlockedIncrement(var);
115}
116
117inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
118    return InterlockedDecrement(var);
119}
120U_NAMESPACE_END
121
122
123#elif U_HAVE_CLANG_ATOMICS
124/*
125 *  Clang __c11 atomic built-ins
126 */
127
128U_NAMESPACE_BEGIN
129typedef _Atomic(int32_t) u_atomic_int32_t;
130#define ATOMIC_INT32_T_INITIALIZER(val) val
131
132inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
133     return __c11_atomic_load(&var, __ATOMIC_ACQUIRE);
134}
135
136inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
137   return __c11_atomic_store(&var, val, __ATOMIC_RELEASE);
138}
139
140inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
141    return __c11_atomic_fetch_add(var, 1, __ATOMIC_SEQ_CST) + 1;
142}
143
144inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
145    return __c11_atomic_fetch_sub(var, 1, __ATOMIC_SEQ_CST) - 1;
146}
147U_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
155U_NAMESPACE_BEGIN
156typedef int32_t u_atomic_int32_t;
157#define ATOMIC_INT32_T_INITIALIZER(val) val
158
159inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
160    int32_t val = var;
161    __sync_synchronize();
162    return val;
163}
164
165inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
166    __sync_synchronize();
167    var = val;
168}
169
170inline int32_t umtx_atomic_inc(u_atomic_int32_t *p)  {
171   return __sync_add_and_fetch(p, 1);
172}
173
174inline int32_t umtx_atomic_dec(u_atomic_int32_t *p)  {
175   return __sync_sub_and_fetch(p, 1);
176}
177U_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
188U_NAMESPACE_BEGIN
189typedef int32_t u_atomic_int32_t;
190#define ATOMIC_INT32_T_INITIALIZER(val) val
191
192U_COMMON_API int32_t U_EXPORT2
193umtx_loadAcquire(u_atomic_int32_t &var);
194
195U_COMMON_API void U_EXPORT2
196umtx_storeRelease(u_atomic_int32_t &var, int32_t val);
197
198U_COMMON_API int32_t U_EXPORT2
199umtx_atomic_inc(u_atomic_int32_t *p);
200
201U_COMMON_API int32_t U_EXPORT2
202umtx_atomic_dec(u_atomic_int32_t *p);
203
204U_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
217U_NAMESPACE_BEGIN
218
219struct UInitOnce {
220    u_atomic_int32_t   fState;
221    UErrorCode       fErrCode;
222    void reset() {fState = 0;};
223    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
231U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &);
232U_COMMON_API void  U_EXPORT2 umtx_initImplPostInit(UInitOnce &);
233
234template<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.
247inline 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.
259inline 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.
278template<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.
290template<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
307U_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
346typedef 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
356struct 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
374struct UMutex {
375    pthread_mutex_t  fMutex;
376};
377typedef struct UMutex UMutex;
378#define U_MUTEX_INITIALIZER  {PTHREAD_MUTEX_INITIALIZER}
379
380struct 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 */
411U_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 */
417U_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
428U_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 */
438U_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 */
444U_INTERNAL void U_EXPORT2 umtx_condSignal(UConditionVar *cond);
445
446#endif /* UMUTEX_H */
447/*eof*/
448