1/*
2**********************************************************************
3*   Copyright (C) 1997-2013, International Business Machines
4*   Corporation and others.  All Rights Reserved.
5**********************************************************************
6*
7* File UMUTEX.H
8*
9* Modification History:
10*
11*   Date        Name        Description
12*   04/02/97  aliu        Creation.
13*   04/07/99  srl         rewrite - C interface, multiple mutices
14*   05/13/99  stephen     Changed to umutex (from cmutex)
15******************************************************************************
16*/
17
18#ifndef UMUTEX_H
19#define UMUTEX_H
20
21#include "unicode/utypes.h"
22#include "unicode/uclean.h"
23#include "putilimp.h"
24
25
26
27// Forward Declarations. UMutex is not in the ICU namespace (yet) because
28//                       there are some remaining references from plain C.
29struct UMutex;
30
31U_NAMESPACE_BEGIN
32struct UInitOnce;
33U_NAMESPACE_END
34
35// Stringify macros, to allow #include of user supplied atomic & mutex files.
36#define U_MUTEX_STR(s) #s
37#define U_MUTEX_XSTR(s) U_MUTEX_STR(s)
38
39/****************************************************************************
40 *
41 *   Low Level Atomic Operations.
42 *      Compiler dependent. Not operating system dependent.
43 *
44 ****************************************************************************/
45#if defined (U_USER_ATOMICS_H)
46#include U_MUTEX_XSTR(U_USER_ATOMICS_H)
47
48#elif U_HAVE_STD_ATOMICS
49
50//  C++11 atomics are available.
51
52#include <atomic>
53
54U_NAMESPACE_BEGIN
55
56typedef std::atomic<int32_t> u_atomic_int32_t;
57#define ATOMIC_INT32_T_INITIALIZER(val) ATOMIC_VAR_INIT(val)
58
59inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
60    return var.load(std::memory_order_acquire);
61}
62
63inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
64    var.store(val, std::memory_order_release);
65}
66
67inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
68    return var->fetch_add(1) + 1;
69}
70
71inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
72    return var->fetch_sub(1) - 1;
73}
74U_NAMESPACE_END
75
76#elif U_PLATFORM_HAS_WIN32_API
77
78// MSVC compiler. Reads and writes of volatile variables have
79//                acquire and release memory semantics, respectively.
80//                This is a Microsoft extension, not standard C++ behavior.
81//
82//   Update:      can't use this because of MinGW, built with gcc.
83//                Original plan was to use gcc atomics for MinGW, but they
84//                aren't supported, so we fold MinGW into this path.
85
86# define WIN32_LEAN_AND_MEAN
87# define VC_EXTRALEAN
88# define NOUSER
89# define NOSERVICE
90# define NOIME
91# define NOMCX
92# ifndef NOMINMAX
93# define NOMINMAX
94# endif
95# include <windows.h>
96U_NAMESPACE_BEGIN
97typedef volatile LONG u_atomic_int32_t;
98#define ATOMIC_INT32_T_INITIALIZER(val) val
99
100inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
101    return InterlockedCompareExchange(&var, 0, 0);
102}
103
104inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
105    InterlockedExchange(&var, val);
106}
107
108
109inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
110    return InterlockedIncrement(var);
111}
112
113inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
114    return InterlockedDecrement(var);
115}
116U_NAMESPACE_END
117
118#elif U_HAVE_GCC_ATOMICS
119/*
120 * gcc atomic ops. These are available on several other compilers as well.
121 */
122
123U_NAMESPACE_BEGIN
124typedef int32_t u_atomic_int32_t;
125#define ATOMIC_INT32_T_INITIALIZER(val) val
126
127inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
128    int32_t val = var;
129    __sync_synchronize();
130    return val;
131}
132
133inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
134    __sync_synchronize();
135    var = val;
136}
137
138inline int32_t umtx_atomic_inc(u_atomic_int32_t *p)  {
139   return __sync_add_and_fetch(p, 1);
140}
141
142inline int32_t umtx_atomic_dec(u_atomic_int32_t *p)  {
143   return __sync_sub_and_fetch(p, 1);
144}
145U_NAMESPACE_END
146
147#else
148
149/*
150 * Unknown Platform. Use out-of-line functions, which in turn use mutexes.
151 *                   Slow but correct.
152 */
153
154#define U_NO_PLATFORM_ATOMICS
155
156U_NAMESPACE_BEGIN
157typedef int32_t u_atomic_int32_t;
158#define ATOMIC_INT32_T_INITIALIZER(val) val
159
160U_COMMON_API int32_t U_EXPORT2
161umtx_loadAcquire(u_atomic_int32_t &var);
162
163U_COMMON_API void U_EXPORT2
164umtx_storeRelease(u_atomic_int32_t &var, int32_t val);
165
166U_COMMON_API int32_t U_EXPORT2
167umtx_atomic_inc(u_atomic_int32_t *p);
168
169U_COMMON_API int32_t U_EXPORT2
170umtx_atomic_dec(u_atomic_int32_t *p);
171
172U_NAMESPACE_END
173
174#endif  /* Low Level Atomic Ops Platfrom Chain */
175
176
177
178/*************************************************************************************************
179 *
180 *  UInitOnce Definitions.
181 *     These are platform neutral.
182 *
183 *************************************************************************************************/
184
185U_NAMESPACE_BEGIN
186
187struct UInitOnce {
188    u_atomic_int32_t   fState;
189    UErrorCode       fErrCode;
190    void reset() {fState = 0;};
191    UBool isReset() {return umtx_loadAcquire(fState) == 0;};
192// Note: isReset() is used by service registration code.
193//                 Thread safety of this usage needs review.
194};
195
196#define U_INITONCE_INITIALIZER {ATOMIC_INT32_T_INITIALIZER(0), U_ZERO_ERROR}
197
198
199U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &);
200U_COMMON_API void  U_EXPORT2 umtx_initImplPostInit(UInitOnce &);
201
202template<class T> void umtx_initOnce(UInitOnce &uio, T *obj, void (T::*fp)()) {
203    if (umtx_loadAcquire(uio.fState) == 2) {
204        return;
205    }
206    if (umtx_initImplPreInit(uio)) {
207        (obj->*fp)();
208        umtx_initImplPostInit(uio);
209    }
210}
211
212
213// umtx_initOnce variant for plain functions, or static class functions.
214//               No context parameter.
215inline void umtx_initOnce(UInitOnce &uio, void (*fp)()) {
216    if (umtx_loadAcquire(uio.fState) == 2) {
217        return;
218    }
219    if (umtx_initImplPreInit(uio)) {
220        (*fp)();
221        umtx_initImplPostInit(uio);
222    }
223}
224
225// umtx_initOnce variant for plain functions, or static class functions.
226//               With ErrorCode, No context parameter.
227inline void umtx_initOnce(UInitOnce &uio, void (*fp)(UErrorCode &), UErrorCode &errCode) {
228    if (U_FAILURE(errCode)) {
229        return;
230    }
231    if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) {
232        // We run the initialization.
233        (*fp)(errCode);
234        uio.fErrCode = errCode;
235        umtx_initImplPostInit(uio);
236    } else {
237        // Someone else already ran the initialization.
238        if (U_FAILURE(uio.fErrCode)) {
239            errCode = uio.fErrCode;
240        }
241    }
242}
243
244// umtx_initOnce variant for plain functions, or static class functions,
245//               with a context parameter.
246template<class T> void umtx_initOnce(UInitOnce &uio, void (*fp)(T), T context) {
247    if (umtx_loadAcquire(uio.fState) == 2) {
248        return;
249    }
250    if (umtx_initImplPreInit(uio)) {
251        (*fp)(context);
252        umtx_initImplPostInit(uio);
253    }
254}
255
256// umtx_initOnce variant for plain functions, or static class functions,
257//               with a context parameter and an error code.
258template<class T> void umtx_initOnce(UInitOnce &uio, void (*fp)(T, UErrorCode &), T context, UErrorCode &errCode) {
259    if (U_FAILURE(errCode)) {
260        return;
261    }
262    if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) {
263        // We run the initialization.
264        (*fp)(context, errCode);
265        uio.fErrCode = errCode;
266        umtx_initImplPostInit(uio);
267    } else {
268        // Someone else already ran the initialization.
269        if (U_FAILURE(uio.fErrCode)) {
270            errCode = uio.fErrCode;
271        }
272    }
273}
274
275U_NAMESPACE_END
276
277
278
279/*************************************************************************************************
280 *
281 *  Mutex Definitions. Platform Dependent, #if platform chain follows.
282 *         TODO:  Add a C++11 version.
283 *                Need to convert all mutex using files to C++ first.
284 *
285 *************************************************************************************************/
286
287#if defined(U_USER_MUTEX_H)
288// #inlcude "U_USER_MUTEX_H"
289#include U_MUTEX_XSTR(U_USER_MUTEX_H)
290
291#elif U_PLATFORM_HAS_WIN32_API
292
293/* Windows Definitions.
294 *    Windows comes first in the platform chain.
295 *    Cygwin (and possibly others) have both WIN32 and POSIX APIs. Prefer Win32 in this case.
296 */
297
298
299/* For CRITICAL_SECTION */
300
301/*
302 *   Note: there is an earlier include of windows.h in this file, but it is in
303 *         different conditionals.
304 *         This one is needed if we are using C++11 for atomic ops, but
305 *         win32 APIs for Critical Sections.
306 */
307
308# define WIN32_LEAN_AND_MEAN
309# define VC_EXTRALEAN
310# define NOUSER
311# define NOSERVICE
312# define NOIME
313# define NOMCX
314# ifndef NOMINMAX
315# define NOMINMAX
316# endif
317# include <windows.h>
318
319
320typedef struct UMutex {
321    icu::UInitOnce    fInitOnce;
322    CRITICAL_SECTION  fCS;
323} UMutex;
324
325/* Initializer for a static UMUTEX. Deliberately contains no value for the
326 *  CRITICAL_SECTION.
327 */
328#define U_MUTEX_INITIALIZER {U_INITONCE_INITIALIZER}
329
330
331
332#elif U_PLATFORM_IMPLEMENTS_POSIX
333
334/*
335 *  POSIX platform
336 */
337
338#include <pthread.h>
339
340struct UMutex {
341    pthread_mutex_t  fMutex;
342};
343typedef struct UMutex UMutex;
344#define U_MUTEX_INITIALIZER  {PTHREAD_MUTEX_INITIALIZER}
345
346#else
347
348/*
349 *  Unknow platform type.
350 *      This is an error condition. ICU requires mutexes.
351 */
352
353#error Unknown Platform.
354
355#endif
356
357
358
359/**************************************************************************************
360 *
361 *  Mutex Implementation function declaratations.
362 *     Declarations are platform neutral.
363 *     Implementations, in umutex.cpp, are platform specific.
364 *
365 ************************************************************************************/
366
367/* Lock a mutex.
368 * @param mutex The given mutex to be locked.  Pass NULL to specify
369 *              the global ICU mutex.  Recursive locks are an error
370 *              and may cause a deadlock on some platforms.
371 */
372U_INTERNAL void U_EXPORT2 umtx_lock(UMutex* mutex);
373
374/* Unlock a mutex.
375 * @param mutex The given mutex to be unlocked.  Pass NULL to specify
376 *              the global ICU mutex.
377 */
378U_INTERNAL void U_EXPORT2 umtx_unlock (UMutex* mutex);
379
380#endif /* UMUTEX_H */
381/*eof*/
382