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 <intrin.h>
26 : #include <math.h> // include math.h early to avoid warning later
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 : /** Perform a full memory barrier. */
37 19539855 : inline void memoryBarrier()
38 : {
39 : #ifdef __GNUC__
40 19539855 : __sync_synchronize();
41 : #elif defined(_MSC_VER)
42 : _ReadWriteBarrier();
43 : #elif defined(__xlC__)
44 : __fence();
45 : __eieio();
46 : __fence();
47 : #else
48 : #error "no memory barrier implemented for this platform"
49 : #endif
50 19539855 : }
51 :
52 : /** Perform a load-with-acquire memory barrier. */
53 17353658 : inline void memoryBarrierAcquire()
54 : {
55 : #ifdef __xlC__
56 : __fence();
57 : __eieio();
58 : #else
59 17353658 : memoryBarrier();
60 : #endif
61 21562421 : }
62 :
63 : /** Perform a store-with-release memory barrier. */
64 : inline void memoryBarrierRelease()
65 : {
66 : #ifdef __xlC__
67 : __isync();
68 : __fence();
69 : #else
70 : memoryBarrier();
71 : #endif
72 : }
73 :
74 : /**
75 : * A variable with atomic semantics and standalone atomic operations.
76 : *
77 : * Use the C++11 equivalent if you can.
78 : *
79 : * Atomic variables can be modified safely from multiple threads
80 : * concurrently. They are useful to implement lock-free algorithms.
81 : *
82 : * For implementation reasons, only signed atomic variables are supported, of
83 : * which int32_t and ssize_t are implemented and typedef'd as a_int32_t and
84 : * a_ssize_t.
85 : */
86 : template <class T>
87 : 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 __GNUC__
172 : template <class T>
173 : T Atomic<T>::getAndAdd(T& value, const T increment)
174 : {
175 : return __sync_fetch_and_add(&value, increment);
176 : }
177 :
178 : template <class T>
179 : T Atomic<T>::getAndSub(T& value, const T increment)
180 : {
181 : return __sync_fetch_and_sub(&value, increment);
182 : }
183 :
184 : template <class T>
185 16036953 : T Atomic<T>::addAndGet(T& value, const T increment)
186 : {
187 16036953 : return __sync_add_and_fetch(&value, increment);
188 : }
189 :
190 : template <class T>
191 16100762 : T Atomic<T>::subAndGet(T& value, const T increment)
192 : {
193 16100762 : return __sync_sub_and_fetch(&value, increment);
194 : }
195 :
196 : template <class T>
197 13365361 : T Atomic<T>::incAndGet(T& value)
198 : {
199 13365361 : return addAndGet(value, 1);
200 : }
201 :
202 : template <class T>
203 15610211 : T Atomic<T>::decAndGet(T& value)
204 : {
205 15610211 : return subAndGet(value, 1);
206 : }
207 :
208 : template <class T>
209 8642 : bool Atomic<T>::compareAndSwap(T* value, const T expected, const T newValue)
210 : {
211 8642 : return __sync_bool_compare_and_swap(value, expected, newValue);
212 : }
213 :
214 : #elif defined(_MSC_VER)
215 :
216 : // see also atomic.cpp
217 : template <class T>
218 : T Atomic<T>::addAndGet(T& value, const T increment)
219 : {
220 : return getAndAdd(value, increment) + increment;
221 : }
222 :
223 : template <class T>
224 : T Atomic<T>::subAndGet(T& value, const T increment)
225 : {
226 : return getAndSub(value, increment) - increment;
227 : }
228 :
229 : #else
230 : #ifdef __xlC__
231 : template <class T>
232 : bool Atomic<T>::compareAndSwap(T* value, const T expected, const T newValue)
233 : {
234 : return __compare_and_swap(value, const_cast<T*>(&expected), newValue);
235 : }
236 : #ifdef __64BIT__
237 : template <>
238 : inline bool Atomic<int64_t>::compareAndSwap(int64_t* value,
239 : const int64_t expected,
240 : const int64_t newValue)
241 : {
242 : return __compare_and_swaplp(value, const_cast<int64_t*>(&expected),
243 : newValue);
244 : }
245 : #endif
246 : #else
247 : #error No compare-and-swap implementated for this platform
248 : #endif
249 :
250 : template <class T>
251 : T Atomic<T>::getAndAdd(T& value, const T increment)
252 : {
253 : for (;;)
254 : {
255 : memoryBarrierAcquire();
256 : const T oldv = value;
257 : const T newv = oldv + increment;
258 : if (!compareAndSwap(&value, oldv, newv))
259 : continue;
260 :
261 : memoryBarrierRelease();
262 : return oldv;
263 : }
264 : }
265 :
266 : template <class T>
267 : T Atomic<T>::getAndSub(T& value, const T increment)
268 : {
269 : for (;;)
270 : {
271 : memoryBarrierAcquire();
272 : const T oldv = value;
273 : const T newv = oldv - increment;
274 : if (!compareAndSwap(&value, oldv, newv))
275 : continue;
276 :
277 : memoryBarrierRelease();
278 : return oldv;
279 : }
280 : }
281 :
282 : template <class T>
283 : T Atomic<T>::addAndGet(T& value, const T increment)
284 : {
285 : for (;;)
286 : {
287 : memoryBarrierAcquire();
288 : const T oldv = value;
289 : const T newv = oldv + increment;
290 : if (!Atomic<T>::compareAndSwap(&value, oldv, newv))
291 : continue;
292 :
293 : memoryBarrierRelease();
294 : return newv;
295 : }
296 : }
297 :
298 : template <class T>
299 : T Atomic<T>::subAndGet(T& value, const T increment)
300 : {
301 : for (;;)
302 : {
303 : memoryBarrierAcquire();
304 : const T oldv = value;
305 : const T newv = oldv - increment;
306 : if (!Atomic<T>::compareAndSwap(&value, oldv, newv))
307 : continue;
308 :
309 : memoryBarrierRelease();
310 : return newv;
311 : }
312 : }
313 :
314 : template <class T>
315 : T Atomic<T>::incAndGet(T& value)
316 : {
317 : return addAndGet(value, 1);
318 : }
319 :
320 : template <class T>
321 : T Atomic<T>::decAndGet(T& value)
322 : {
323 : return subAndGet(value, 1);
324 : }
325 : #endif
326 :
327 : template <class T>
328 2000112 : Atomic<T>::Atomic(const T v)
329 2000112 : : _value(v)
330 : {
331 2000112 : }
332 :
333 : template <class T>
334 : Atomic<T>::Atomic(const Atomic<T>& v)
335 : : _value(v._value)
336 : {
337 : }
338 :
339 : template <class T>
340 17746772 : Atomic<T>::operator T(void) const
341 : {
342 17746772 : memoryBarrierAcquire();
343 19827055 : return _value;
344 : }
345 :
346 : template <class T>
347 556303 : void Atomic<T>::operator=(const T v)
348 : {
349 556303 : _value = v;
350 556303 : memoryBarrier();
351 556347 : }
352 :
353 : template <class T>
354 : void Atomic<T>::operator=(const Atomic<T>& v)
355 : {
356 : _value = v._value;
357 : memoryBarrier();
358 : }
359 :
360 : template <class T>
361 : T Atomic<T>::operator+=(T v)
362 : {
363 : return addAndGet(_value, v);
364 : }
365 :
366 : template <class T>
367 : T Atomic<T>::operator-=(T v)
368 : {
369 : return subAndGet(_value, v);
370 : }
371 :
372 : template <class T>
373 13592321 : T Atomic<T>::operator++(void)
374 : {
375 13592321 : return incAndGet(_value);
376 : }
377 :
378 : template <class T>
379 15762243 : T Atomic<T>::operator--(void)
380 : {
381 15762243 : return decAndGet(_value);
382 : }
383 :
384 : template <class T>
385 : T Atomic<T>::operator++(int)
386 : {
387 : return getAndAdd(_value, 1);
388 : }
389 :
390 : template <class T>
391 : T Atomic<T>::operator--(int)
392 : {
393 : return getAndSub(_value, 1);
394 : }
395 :
396 : template <class T>
397 547262 : bool Atomic<T>::operator==(const Atomic<T>& rhs) const
398 : {
399 547262 : memoryBarrier();
400 547262 : return _value == rhs._value;
401 : }
402 :
403 : template <class T>
404 : bool Atomic<T>::operator!=(const Atomic<T>& rhs) const
405 : {
406 : memoryBarrier();
407 : return _value != rhs._value;
408 : }
409 :
410 : template <class T>
411 8599 : bool Atomic<T>::compareAndSwap(const T expected, const T newValue)
412 : {
413 8599 : return compareAndSwap(&_value, expected, newValue);
414 : }
415 : }
416 : #endif // LUNCHBOX_ATOMIC_H
|