7872fc056eec9ba9dc84ac9f4d52ab559daa66df
[libcds.git] / cds / algo / atomic.h
1 //$$CDS-header$$
2
3 #ifndef __CDS_CXX11_ATOMIC_H
4 #define __CDS_CXX11_ATOMIC_H
5
6 #include <cds/details/defs.h>
7
8 namespace cds {
9
10 /// C++11 Atomic library support
11 /** @anchor cds_cxx11_atomic
12     \p libcds can use the following implementations of the atomics:
13     - STL \p &lt;atomic&gt;. This is used by default
14     - \p boost.atomic for boost 1.54 and above. To use it you should define \p CDS_USE_BOOST_ATOMIC for
15       your compiler invocation, for example, for gcc specify \p -DCDS_USE_BOOST_ATOMIC
16       in command line
17     - \p libcds implementation of atomic operation according to C++11 standard as
18       specified in <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf">N3242, p.29</a>.
19       \p libcds implementation is not the full standard compliant, it provides only C++ part of standard,
20       for example, \p libcds has no static initialization of the atomic variables and some other C features.
21       However, that imlementation is enough for the library purposes. Supported architecture: x86, amd64,
22       ia64 (Itanium) 64bit, 64bit Sparc. To use \p libcds atomic you should define \p CDS_USE_LIBCDS_ATOMIC
23       in the compiler command line (\p -DCDS_USE_LIBCDS_ATOMIC for gcc/clang).
24
25       @note For Clang compiler \p libcds doesn't use native \p libc++ \p &lt;atomic&gt; due some problems.
26       Instead, \p libcds atomic is used by default, or you can try to use \p boost.atomic.
27
28       The library defines \p atomics alias for atomic namespace:
29       - <tt>namespace atomics = std</tt> for STL
30       - <tt>namespace atomics = boost</tt> for \p boost.atomic
31       - <tt>namespace atomics = cds::cxx11_atomic</tt> for library-provided atomic implementation
32 */
33 namespace cxx11_atomic {
34 }} // namespace cds::cxx11_atomic
35
36 //@cond
37 #if defined(CDS_USE_BOOST_ATOMIC)
38     // boost atomic
39 #   include <boost/version.hpp>
40 #   if BOOST_VERSION >= 105400
41 #       include <boost/atomic.hpp>
42         namespace atomics = boost;
43 #       define CDS_CXX11_ATOMIC_BEGIN_NAMESPACE namespace boost {
44 #       define CDS_CXX11_ATOMIC_END_NAMESPACE }
45 #   else
46 #       error "Boost version 1.54 or above is needed for boost.atomic"
47 #   endif
48 #elif defined(CDS_USE_LIBCDS_ATOMIC)
49     // libcds atomic
50 #   include <cds/compiler/cxx11_atomic.h>
51     namespace atomics = cds::cxx11_atomic;
52 #   define CDS_CXX11_ATOMIC_BEGIN_NAMESPACE namespace cds { namespace cxx11_atomic {
53 #   define CDS_CXX11_ATOMIC_END_NAMESPACE }}
54 #else
55     // Compiler provided C++11 atomic
56 #   include <atomic>
57     namespace atomics = std;
58 #   define CDS_CXX11_ATOMIC_BEGIN_NAMESPACE namespace std {
59 #   define CDS_CXX11_ATOMIC_END_NAMESPACE }
60 #endif
61 //@endcond
62
63 namespace cds {
64
65     /// Atomic primitives
66     /**
67         This namespace contains useful primitives derived from <tt>std::atomic</tt>.
68     */
69     namespace atomicity {
70
71         /// Atomic event counter.
72         /**
73             This class is based on <tt>std::atomic_size_t</tt>.
74             It uses relaxed memory ordering \p memory_order_relaxed and may be used as a statistic counter.
75         */
76         class event_counter
77         {
78             //@cond
79             atomics::atomic_size_t   m_counter;
80             //@endcond
81
82         public:
83             typedef size_t      value_type  ;       ///< Type of counter
84
85         public:
86             // Initializes event counter with zero
87             event_counter() CDS_NOEXCEPT
88                 : m_counter(size_t(0))
89             {}
90
91             /// Assign operator
92             /**
93                 Returns \p n.
94             */
95             value_type operator =(
96                 value_type n    //< new value of the counter
97             ) CDS_NOEXCEPT
98             {
99                 m_counter.exchange( n, atomics::memory_order_relaxed );
100                 return n;
101             }
102
103             /// Addition
104             /**
105                 Returns new value of the atomic counter.
106             */
107             size_t operator +=(
108                 size_t n    ///< addendum
109             ) CDS_NOEXCEPT
110             {
111                 return m_counter.fetch_add( n, atomics::memory_order_relaxed ) + n;
112             }
113
114             /// Substraction
115             /**
116                 Returns new value of the atomic counter.
117             */
118             size_t operator -=(
119                 size_t n    ///< subtrahend
120             ) CDS_NOEXCEPT
121             {
122                 return m_counter.fetch_sub( n, atomics::memory_order_relaxed ) - n;
123             }
124
125             /// Get current value of the counter
126             operator size_t () const CDS_NOEXCEPT
127             {
128                 return m_counter.load( atomics::memory_order_relaxed );
129             }
130
131             /// Preincrement
132             size_t operator ++() CDS_NOEXCEPT
133             {
134                 return m_counter.fetch_add( 1, atomics::memory_order_relaxed ) + 1;
135             }
136             /// Postincrement
137             size_t operator ++(int) CDS_NOEXCEPT
138             {
139                 return m_counter.fetch_add( 1, atomics::memory_order_relaxed );
140             }
141
142             /// Predecrement
143             size_t operator --() CDS_NOEXCEPT
144             {
145                 return m_counter.fetch_sub( 1, atomics::memory_order_relaxed ) - 1;
146             }
147             /// Postdecrement
148             size_t operator --(int) CDS_NOEXCEPT
149             {
150                 return m_counter.fetch_sub( 1, atomics::memory_order_relaxed );
151             }
152
153             /// Get current value of the counter
154             size_t get() const CDS_NOEXCEPT
155             {
156                 return m_counter.load( atomics::memory_order_relaxed );
157             }
158
159             /// Resets the counter to 0
160             void reset() CDS_NOEXCEPT
161             {
162                 m_counter.store( 0, atomics::memory_order_release );
163             }
164
165         };
166
167         /// Atomic item counter
168         /**
169             This class is simplified interface around <tt>std::atomic_size_t</tt>.
170             The class supports getting of current value of the counter and increment/decrement its value.
171         */
172         class item_counter
173         {
174         public:
175             typedef atomics::atomic_size_t   atomic_type ;   ///< atomic type used
176             typedef size_t counter_type    ;   ///< Integral item counter type (size_t)
177
178         private:
179             //@cond
180             atomic_type                         m_Counter   ;   ///< Atomic item counter
181             //@endcond
182
183         public:
184             /// Default ctor initializes the counter to zero.
185             item_counter()
186                 : m_Counter(counter_type(0))
187             {}
188
189             /// Returns current value of the counter
190             counter_type    value(atomics::memory_order order = atomics::memory_order_relaxed) const
191             {
192                 return m_Counter.load( order );
193             }
194
195             /// Same as \ref value() with relaxed memory ordering
196             operator counter_type() const
197             {
198                 return value();
199             }
200
201             /// Returns underlying atomic interface
202             atomic_type&  getAtomic()
203             {
204                 return m_Counter;
205             }
206
207             /// Returns underlying atomic interface (const)
208             const atomic_type&  getAtomic() const
209             {
210                 return m_Counter;
211             }
212
213             /// Increments the counter. Semantics: postincrement
214             counter_type inc(atomics::memory_order order = atomics::memory_order_relaxed )
215             {
216                 return m_Counter.fetch_add( 1, order );
217             }
218
219             /// Decrements the counter. Semantics: postdecrement
220             counter_type dec(atomics::memory_order order = atomics::memory_order_relaxed)
221             {
222                 return m_Counter.fetch_sub( 1, order );
223             }
224
225             /// Preincrement
226             counter_type operator ++()
227             {
228                 return inc() + 1;
229             }
230             /// Postincrement
231             counter_type operator ++(int)
232             {
233                 return inc();
234             }
235
236             /// Predecrement
237             counter_type operator --()
238             {
239                 return dec() - 1;
240             }
241             /// Postdecrement
242             counter_type operator --(int)
243             {
244                 return dec();
245             }
246
247             /// Resets count to 0
248             void reset(atomics::memory_order order = atomics::memory_order_relaxed)
249             {
250                 m_Counter.store( 0, order );
251             }
252         };
253
254         /// Empty item counter
255         /**
256             This class may be used instead of \ref item_counter when you do not need full \ref item_counter interface.
257             All methods of the class is empty and returns 0.
258
259             The object of this class should not be used in data structure that behavior significantly depends on item counting
260             (for example, in many hash map implementation).
261         */
262         class empty_item_counter {
263         public:
264             typedef size_t counter_type    ;  ///< Counter type
265         public:
266             /// Returns 0
267             counter_type    value(atomics::memory_order /*order*/ = atomics::memory_order_relaxed) const
268             {
269                 return 0;
270             }
271
272             /// Same as \ref value(), always returns 0.
273             operator counter_type() const
274             {
275                 return value();
276             }
277
278             /// Dummy increment. Always returns 0
279             size_t inc(atomics::memory_order /*order*/ = atomics::memory_order_relaxed)
280             {
281                 return 0;
282             }
283
284             /// Dummy increment. Always returns 0
285             size_t dec(atomics::memory_order /*order*/ = atomics::memory_order_relaxed)
286             {
287                 return 0;
288             }
289
290             /// Dummy pre-increment. Always returns 0
291             size_t operator ++()
292             {
293                 return 0;
294             }
295             /// Dummy post-increment. Always returns 0
296             size_t operator ++(int)
297             {
298                 return 0;
299             }
300
301             /// Dummy pre-decrement. Always returns 0
302             size_t operator --()
303             {
304                 return 0;
305             }
306             /// Dummy post-decrement. Always returns 0
307             size_t operator --(int)
308             {
309                 return 0;
310             }
311
312             /// Dummy function
313             void reset(atomics::memory_order /*order*/ = atomics::memory_order_relaxed)
314             {}
315         };
316     }   // namespace atomicity
317 }   // namespace cds
318
319 #endif // #ifndef __CDS_CXX11_ATOMIC_H