Line data Source code
1 :
2 : // Copyright (C) 2007, 2008 Tim Blechmann & Thomas Grill
3 : //
4 : // Distributed under the Boost Software License, Version 1.0. (See
5 : // accompanying file LICENSE_1_0.txt or copy at
6 : // http://www.boost.org/LICENSE_1_0.txt)
7 :
8 : // Disclaimer: Not a Boost library.
9 :
10 : /* Copyright (c) 2008-2012, Stefan Eilemann <eile@equalizergraphics.com>
11 : Modifications to use within lunchbox namespace and naming conventions.
12 : Original at http://tim.klingt.org/git?p=boost_lockfree.git;a=tree
13 : */
14 :
15 : #ifndef LUNCHBOX_ATOMIC_H
16 : #define LUNCHBOX_ATOMIC_H
17 :
18 : #include <lunchbox/api.h>
19 : #include <lunchbox/compiler.h> // GCC version
20 : #include <lunchbox/types.h>
21 :
22 : #ifdef _MSC_VER
23 : # pragma warning (push)
24 : # pragma warning (disable: 4985) // inconsistent decl of ceil
25 : # include <math.h> // include math.h early to avoid warning later
26 : # include <intrin.h>
27 : # pragma warning (pop)
28 : # pragma intrinsic(_ReadWriteBarrier)
29 : #elif defined(__xlC__)
30 : # include <builtins.h>
31 : # include <iostream>
32 : #endif
33 :
34 : namespace lunchbox
35 : {
36 :
37 : /** Perform a full memory barrier. */
38 251720363 : inline void memoryBarrier()
39 : {
40 : #ifdef LB_GCC_4_1_OR_LATER
41 251720363 : __sync_synchronize();
42 : #elif defined(_MSC_VER)
43 : _ReadWriteBarrier();
44 : #elif defined(__xlC__)
45 : __fence();
46 : __eieio();
47 : __fence();
48 : #else
49 : # error "no memory barrier implemented for this platform"
50 : #endif
51 251720363 : }
52 :
53 : /** Perform a load-with-acquire memory barrier. */
54 185324294 : inline void memoryBarrierAcquire()
55 : {
56 : #ifdef __xlC__
57 : __fence();
58 : __eieio();
59 : #else
60 185324294 : memoryBarrier();
61 : #endif
62 220079979 : }
63 :
64 : /** Perform a store-with-release memory barrier. */
65 : inline void memoryBarrierRelease()
66 : {
67 : #ifdef __xlC__
68 : __isync();
69 : __fence();
70 : #else
71 : memoryBarrier();
72 : #endif
73 : }
74 :
75 : /**
76 : * A variable with atomic semantics and standalone atomic operations.
77 : *
78 : * Use the C++11 equivalent if you can.
79 : *
80 : * Atomic variables can be modified safely from multiple threads
81 : * concurrently. They are useful to implement lock-free algorithms.
82 : *
83 : * For implementation reasons, only signed atomic variables are supported, of
84 : * which int32_t and ssize_t are implemented and typedef'd as a_int32_t and
85 : * a_ssize_t.
86 : */
87 : template< class T > class Atomic
88 : {
89 : public:
90 : /** @return the old value, then add the given increment. */
91 : LUNCHBOX_API static T getAndAdd( T& value, const T increment );
92 :
93 : /** @return the old value, then substract the increment. */
94 : LUNCHBOX_API static T getAndSub( T& value, const T increment );
95 :
96 : /** @return the new value after adding the given increment. */
97 : static T addAndGet( T& value, const T increment );
98 :
99 : /** @return the new value after substracting the increment. */
100 : static T subAndGet( T& value, const T increment );
101 :
102 : /** @return the new value after incrementing the value. */
103 : LUNCHBOX_API static T incAndGet( T& value );
104 :
105 : /** @return the new value after decrementing the value. */
106 : LUNCHBOX_API static T decAndGet( T& value );
107 :
108 : /** Perform a compare-and-swap atomic operation. */
109 : LUNCHBOX_API static bool compareAndSwap( T* value, const T expected,
110 : const T newValue );
111 :
112 : /** Construct a new atomic variable with an initial value. @version 1.0 */
113 : explicit Atomic( const T v = 0 );
114 :
115 : /** Construct a copy of an atomic variable. Not thread-safe! @version 1.0 */
116 : Atomic( const Atomic< T >& v );
117 :
118 : /** @return the current value @version 1.0 */
119 : operator T(void) const;
120 :
121 : /** Assign a new value @version 1.0 */
122 : void operator = ( const T v );
123 :
124 : /** Assign a new value. Not thread-safe! @version 1.0 */
125 : void operator = ( const Atomic< T >& v);
126 :
127 : /** Atomically add a value and return the new value. @version 1.0 */
128 : T operator +=(T v);
129 :
130 : /** Atomically substract a value and return the new value. @version 1.0 */
131 : T operator -=(T v);
132 :
133 : /** Atomically increment by one and return the new value. @version 1.0 */
134 : T operator ++(void);
135 :
136 : /** Atomically decrement by one and return the new value. @version 1.0 */
137 : T operator --(void);
138 :
139 : /** Atomically increment by one and return the old value. @version 1.0 */
140 : T operator ++(int);
141 :
142 : /** Atomically decrement by one and return the old value. @version 1.0 */
143 : T operator --(int);
144 :
145 : /** @return true if the variable has the given value. @version 1.1.2 */
146 : bool operator == ( const Atomic< T >& rhs ) const;
147 :
148 : /** @return true if the variable has not the given value. @version 1.1.2 */
149 : bool operator != ( const Atomic< T >& rhs ) const;
150 :
151 : /**
152 : * Perform a compare-and-swap atomic operation.
153 : *
154 : * Atomically replaces the value and return true if the value matched the
155 : * expected.
156 : * @return true if the new value was set, false otherwise
157 : * @version 1.1.2
158 : */
159 : bool compareAndSwap( const T expected, const T newValue );
160 :
161 : private:
162 : // https://github.com/Eyescale/Lunchbox/issues/8
163 : #if _MSC_VER < 1700
164 : mutable T _value;
165 : #else
166 : LB_ALIGN8( mutable T _value );
167 : #endif
168 : };
169 :
170 : // Implementation
171 : #ifdef LB_GCC_4_1_OR_LATER
172 : template< class T > T Atomic< T >::getAndAdd( T& value, const T increment )
173 : {
174 : return __sync_fetch_and_add( &value, increment );
175 : }
176 :
177 : template< class T > T Atomic< T >::getAndSub( T& value, const T increment )
178 : {
179 : return __sync_fetch_and_sub( &value, increment );
180 : }
181 :
182 10495596 : template< class T > T Atomic< T >::addAndGet( T& value, const T increment )
183 : {
184 10495596 : return __sync_add_and_fetch( &value, increment );
185 : }
186 :
187 10669588 : template< class T > T Atomic< T >::subAndGet( T& value, const T increment )
188 : {
189 10669588 : return __sync_sub_and_fetch( &value, increment );
190 : }
191 :
192 7438008 : template< class T > T Atomic< T >::incAndGet( T& value )
193 : {
194 7438008 : return addAndGet( value, 1 );
195 : }
196 :
197 7800294 : template< class T > T Atomic< T >::decAndGet( T& value )
198 : {
199 7800294 : return subAndGet( value, 1 );
200 : }
201 :
202 : template< class T >
203 203134326 : bool Atomic< T >::compareAndSwap( T* value, const T expected, const T newValue )
204 : {
205 203134326 : return __sync_bool_compare_and_swap( value, expected, newValue );
206 : }
207 :
208 : #elif defined (_MSC_VER)
209 :
210 : // see also atomic.cpp
211 : template< class T > T Atomic< T >::addAndGet( T& value, const T increment )
212 : {
213 : return getAndAdd( value, increment ) + increment;
214 : }
215 :
216 : template< class T > T Atomic< T >::subAndGet( T& value, const T increment )
217 : {
218 : return getAndSub( value, increment ) - increment;
219 : }
220 :
221 : #else
222 : # ifdef __xlC__
223 : template< class T >
224 : bool Atomic< T >::compareAndSwap( T* value, const T expected, const T newValue )
225 : {
226 : return __compare_and_swap( value, const_cast< T* >( &expected ), newValue );
227 : }
228 : # ifdef __64BIT__
229 : template<> inline
230 : bool Atomic< int64_t >::compareAndSwap( int64_t* value, const int64_t expected,
231 : const int64_t newValue )
232 : {
233 : return __compare_and_swaplp( value, const_cast< int64_t* >( &expected ),
234 : newValue );
235 : }
236 : # endif
237 : # else
238 : # error No compare-and-swap implementated for this platform
239 : # endif
240 :
241 : template< class T > T Atomic< T >::getAndAdd( T& value, const T increment )
242 : {
243 : for(;;)
244 : {
245 : memoryBarrierAcquire();
246 : const T oldv = value;
247 : const T newv = oldv + increment;
248 : if( !compareAndSwap( &value, oldv, newv ))
249 : continue;
250 :
251 : memoryBarrierRelease();
252 : return oldv;
253 : }
254 : }
255 :
256 : template< class T > T Atomic< T >::getAndSub( T& value, const T increment )
257 : {
258 : for(;;)
259 : {
260 : memoryBarrierAcquire();
261 : const T oldv = value;
262 : const T newv = oldv - increment;
263 : if( !compareAndSwap( &value, oldv, newv ))
264 : continue;
265 :
266 : memoryBarrierRelease();
267 : return oldv;
268 : }
269 : }
270 :
271 : template< class T > T Atomic< T >::addAndGet( T& value, const T increment )
272 : {
273 : for(;;)
274 : {
275 : memoryBarrierAcquire();
276 : const T oldv = value;
277 : const T newv = oldv + increment;
278 : if( !Atomic< T >::compareAndSwap( &value, oldv, newv ))
279 : continue;
280 :
281 : memoryBarrierRelease();
282 : return newv;
283 : }
284 : }
285 :
286 : template< class T > T Atomic< T >::subAndGet( T& value, const T increment )
287 : {
288 : for(;;)
289 : {
290 : memoryBarrierAcquire();
291 : const T oldv = value;
292 : const T newv = oldv - increment;
293 : if( !Atomic< T >::compareAndSwap( &value, oldv, newv ))
294 : continue;
295 :
296 : memoryBarrierRelease();
297 : return newv;
298 : }
299 : }
300 :
301 : template< class T > T Atomic< T >::incAndGet( T& value )
302 : {
303 : return addAndGet( value, 1 );
304 : }
305 :
306 : template< class T > T Atomic< T >::decAndGet( T& value )
307 : {
308 : return subAndGet( value, 1 );
309 : }
310 : #endif
311 :
312 500 : template< class T > Atomic< T >::Atomic ( const T v ) : _value(v) {}
313 :
314 : template <class T>
315 : Atomic< T >::Atomic( const Atomic< T >& v ) : _value( v._value ) {}
316 :
317 : template <class T>
318 188232817 : Atomic< T >::operator T(void) const
319 : {
320 188232817 : memoryBarrierAcquire();
321 200414578 : return _value;
322 : }
323 :
324 39577824 : template< class T > void Atomic< T >::operator = ( const T v )
325 : {
326 39577824 : _value = v;
327 39577824 : memoryBarrier();
328 39577791 : }
329 :
330 : template< class T > void Atomic< T >::operator = ( const Atomic< T >& v)
331 : {
332 : _value = v._value;
333 : memoryBarrier();
334 : }
335 :
336 : template< class T > T Atomic< T >::operator += (T v)
337 : {
338 : return addAndGet( _value, v );
339 : }
340 :
341 : template< class T > T Atomic< T >::operator -=(T v)
342 : {
343 : return subAndGet( _value, v );
344 : }
345 :
346 7262156 : template< class T > T Atomic< T >::operator ++(void)
347 : {
348 7262156 : return incAndGet( _value );
349 : }
350 :
351 7640300 : template< class T > T Atomic< T >::operator --(void)
352 : {
353 7640300 : return decAndGet( _value );
354 : }
355 :
356 : template< class T > T Atomic< T >::operator ++(int)
357 : {
358 : return getAndAdd( _value, 1 );
359 : }
360 :
361 : template< class T > T Atomic< T >::operator --(int)
362 : {
363 : return getAndSub( _value, 1 );
364 : }
365 :
366 773547 : template< class T > bool Atomic< T >::operator == ( const Atomic<T>& rhs ) const
367 : {
368 773547 : memoryBarrier();
369 773547 : return _value == rhs._value;
370 : }
371 :
372 : template< class T > bool Atomic< T >::operator != ( const Atomic<T>& rhs ) const
373 : {
374 : memoryBarrier();
375 : return _value != rhs._value;
376 : }
377 :
378 : template< class T >
379 169352594 : bool Atomic< T >::compareAndSwap( const T expected, const T newValue )
380 : {
381 169352594 : return compareAndSwap( &_value, expected, newValue );
382 : }
383 :
384 : }
385 : #endif // LUNCHBOX_ATOMIC_H
|