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>
96
97U_NAMESPACE_BEGIN
98typedef volatile LONG u_atomic_int32_t;
99#define ATOMIC_INT32_T_INITIALIZER(val) val
100
101inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
102    return InterlockedCompareExchange(&var, 0, 0);
103}
104
105inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
106    InterlockedExchange(&var, val);
107}
108
109
110inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
111    return InterlockedIncrement(var);
112}
113
114inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
115    return InterlockedDecrement(var);
116}
117U_NAMESPACE_END
118
119
120#elif U_HAVE_GCC_ATOMICS
121/*
122 * gcc atomic ops. These are available on several other compilers as well.
123 */
124
125U_NAMESPACE_BEGIN
126typedef int32_t u_atomic_int32_t;
127#define ATOMIC_INT32_T_INITIALIZER(val) val
128
129inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
130    int32_t val = var;
131    __sync_synchronize();
132    return val;
133}
134
135inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
136    __sync_synchronize();
137    var = val;
138}
139
140inline int32_t umtx_atomic_inc(u_atomic_int32_t *p)  {
141   return __sync_add_and_fetch(p, 1);
142}
143
144inline int32_t umtx_atomic_dec(u_atomic_int32_t *p)  {
145   return __sync_sub_and_fetch(p, 1);
146}
147U_NAMESPACE_END
148
149#else
150
151/*
152 * Unknown Platform. Use out-of-line functions, which in turn use mutexes.
153 *                   Slow but correct.
154 */
155
156#define U_NO_PLATFORM_ATOMICS
157
158U_NAMESPACE_BEGIN
159typedef int32_t u_atomic_int32_t;
160#define ATOMIC_INT32_T_INITIALIZER(val) val
161
162U_COMMON_API int32_t U_EXPORT2
163umtx_loadAcquire(u_atomic_int32_t &var);
164
165U_COMMON_API void U_EXPORT2
166umtx_storeRelease(u_atomic_int32_t &var, int32_t val);
167
168U_COMMON_API int32_t U_EXPORT2
169umtx_atomic_inc(u_atomic_int32_t *p);
170
171U_COMMON_API int32_t U_EXPORT2
172umtx_atomic_dec(u_atomic_int32_t *p);
173
174U_NAMESPACE_END
175
176#endif  /* Low Level Atomic Ops Platfrom Chain */
177
178
179
180/*************************************************************************************************
181 *
182 *  UInitOnce Definitions.
183 *     These are platform neutral.
184 *
185 *************************************************************************************************/
186
187U_NAMESPACE_BEGIN
188
189struct UInitOnce {
190    u_atomic_int32_t   fState;
191    UErrorCode       fErrCode;
192    void reset() {fState = 0;};
193    UBool isReset() {return umtx_loadAcquire(fState) == 0;};
194// Note: isReset() is used by service registration code.
195//                 Thread safety of this usage needs review.
196};
197
198#define U_INITONCE_INITIALIZER {ATOMIC_INT32_T_INITIALIZER(0), U_ZERO_ERROR}
199
200
201U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &);
202U_COMMON_API void  U_EXPORT2 umtx_initImplPostInit(UInitOnce &);
203
204template<class T> void umtx_initOnce(UInitOnce &uio, T *obj, void (T::*fp)()) {
205    if (umtx_loadAcquire(uio.fState) == 2) {
206        return;
207    }
208    if (umtx_initImplPreInit(uio)) {
209        (obj->*fp)();
210        umtx_initImplPostInit(uio);
211    }
212}
213
214
215// umtx_initOnce variant for plain functions, or static class functions.
216//               No context parameter.
217inline void umtx_initOnce(UInitOnce &uio, void (*fp)()) {
218    if (umtx_loadAcquire(uio.fState) == 2) {
219        return;
220    }
221    if (umtx_initImplPreInit(uio)) {
222        (*fp)();
223        umtx_initImplPostInit(uio);
224    }
225}
226
227// umtx_initOnce variant for plain functions, or static class functions.
228//               With ErrorCode, No context parameter.
229inline void umtx_initOnce(UInitOnce &uio, void (*fp)(UErrorCode &), UErrorCode &errCode) {
230    if (U_FAILURE(errCode)) {
231        return;
232    }
233    if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) {
234        // We run the initialization.
235        (*fp)(errCode);
236        uio.fErrCode = errCode;
237        umtx_initImplPostInit(uio);
238    } else {
239        // Someone else already ran the initialization.
240        if (U_FAILURE(uio.fErrCode)) {
241            errCode = uio.fErrCode;
242        }
243    }
244}
245
246// umtx_initOnce variant for plain functions, or static class functions,
247//               with a context parameter.
248template<class T> void umtx_initOnce(UInitOnce &uio, void (*fp)(T), T context) {
249    if (umtx_loadAcquire(uio.fState) == 2) {
250        return;
251    }
252    if (umtx_initImplPreInit(uio)) {
253        (*fp)(context);
254        umtx_initImplPostInit(uio);
255    }
256}
257
258// umtx_initOnce variant for plain functions, or static class functions,
259//               with a context parameter and an error code.
260template<class T> void umtx_initOnce(UInitOnce &uio, void (*fp)(T, UErrorCode &), T context, UErrorCode &errCode) {
261    if (U_FAILURE(errCode)) {
262        return;
263    }
264    if (umtx_loadAcquire(uio.fState) != 2 && umtx_initImplPreInit(uio)) {
265        // We run the initialization.
266        (*fp)(context, errCode);
267        uio.fErrCode = errCode;
268        umtx_initImplPostInit(uio);
269    } else {
270        // Someone else already ran the initialization.
271        if (U_FAILURE(uio.fErrCode)) {
272            errCode = uio.fErrCode;
273        }
274    }
275}
276
277U_NAMESPACE_END
278
279
280
281/*************************************************************************************************
282 *
283 *  Mutex Definitions. Platform Dependent, #if platform chain follows.
284 *         TODO:  Add a C++11 version.
285 *                Need to convert all mutex using files to C++ first.
286 *
287 *************************************************************************************************/
288
289#if defined(U_USER_MUTEX_H)
290// #inlcude "U_USER_MUTEX_H"
291#include U_MUTEX_XSTR(U_USER_MUTEX_H)
292
293#elif U_PLATFORM_HAS_WIN32_API
294
295/* Windows Definitions.
296 *    Windows comes first in the platform chain.
297 *    Cygwin (and possibly others) have both WIN32 and POSIX APIs. Prefer Win32 in this case.
298 */
299
300
301/* For CRITICAL_SECTION */
302
303/*
304 *   Note: there is an earlier include of windows.h in this file, but it is in
305 *         different conditionals.
306 *         This one is needed if we are using C++11 for atomic ops, but
307 *         win32 APIs for Critical Sections.
308 */
309
310# define WIN32_LEAN_AND_MEAN
311# define VC_EXTRALEAN
312# define NOUSER
313# define NOSERVICE
314# define NOIME
315# define NOMCX
316# ifndef NOMINMAX
317# define NOMINMAX
318# endif
319# include <windows.h>
320
321
322typedef struct UMutex {
323    icu::UInitOnce         fInitOnce;
324    CRITICAL_SECTION  fCS;
325} UMutex;
326
327/* Initializer for a static UMUTEX. Deliberately contains no value for the
328 *  CRITICAL_SECTION.
329 */
330#define U_MUTEX_INITIALIZER {U_INITONCE_INITIALIZER}
331
332
333
334#elif U_PLATFORM_IMPLEMENTS_POSIX
335
336/*
337 *  POSIX platform
338 */
339
340#include <pthread.h>
341
342struct UMutex {
343    pthread_mutex_t  fMutex;
344};
345typedef struct UMutex UMutex;
346#define U_MUTEX_INITIALIZER  {PTHREAD_MUTEX_INITIALIZER}
347
348#else
349
350/*
351 *  Unknow platform type.
352 *      This is an error condition. ICU requires mutexes.
353 */
354
355#error Unknown Platform.
356
357#endif
358
359
360
361/**************************************************************************************
362 *
363 *  Mutex Implementation function declaratations.
364 *     Declarations are platform neutral.
365 *     Implementations, in umutex.cpp, are platform specific.
366 *
367 ************************************************************************************/
368
369/* Lock a mutex.
370 * @param mutex The given mutex to be locked.  Pass NULL to specify
371 *              the global ICU mutex.  Recursive locks are an error
372 *              and may cause a deadlock on some platforms.
373 */
374U_INTERNAL void U_EXPORT2 umtx_lock(UMutex* mutex);
375
376/* Unlock a mutex.
377 * @param mutex The given mutex to be unlocked.  Pass NULL to specify
378 *              the global ICU mutex.
379 */
380U_INTERNAL void U_EXPORT2 umtx_unlock (UMutex* mutex);
381
382#endif /* UMUTEX_H */
383/*eof*/
384