libqi-api  2.1.4.13
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
atomic.hpp
Go to the documentation of this file.
1 #pragma once
2 /*
3  * Copyright (c) 2012 Aldebaran Robotics. All rights reserved.
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the COPYING file.
6  */
7 
8 
9 #ifndef _QI_ATOMIC_HPP_
10 #define _QI_ATOMIC_HPP_
11 
12 #ifdef _MSC_VER
13 # include <windows.h>
14 # include <intrin.h>
15 
16 extern "C" long __cdecl _InterlockedIncrement(long volatile *);
17 extern "C" long __cdecl _InterlockedDecrement(long volatile *);
18 
19 # pragma intrinsic(_InterlockedIncrement)
20 # pragma intrinsic(_InterlockedDecrement)
21 
22 #endif
23 
24 #include <boost/static_assert.hpp>
25 
26 #include <qi/config.hpp>
27 #include <qi/macro.hpp>
28 
29 namespace qi
30 {
31  inline long testAndSet(long* cond)
32  {
33 #if defined __GNUC__
34  return __sync_bool_compare_and_swap(cond, 0, 1);
35 #elif defined _MSC_VER
36  return 1 - InterlockedCompareExchange(cond, 1, 0);
37 #else
38  #error "Unknown platform, testAndSet not implemented"
39 #endif
40  }
41 
42  /* /!\ WARNING
43  * The 'volatile' is needed even though we use atomic compiler builtins.
44  * Without the volatile, a thread doing
45  * while (!setIfEquals(1,1))
46  * Is never unstuck by a thread doing
47  * setIfEquals(0,1)
48  *
49  * AtomicBase has public member so that it can be initialized at
50  * static-initialization time (to make thread-safe static initialization inside
51  * functions)
52  */
53  template <typename T>
54  struct AtomicBase
55  {
56  public:
57 
58 
59  /* prefix operators */
60  inline T operator++();
61  inline T operator--();
62  inline AtomicBase<T>& operator=(T value);
66  inline bool setIfEquals(T testValue, T setValue);
67 
68  inline T swap(T value);
69 
70  inline T operator*() const
71  {
72  return _value;
73  }
74 
75  public:
76  BOOST_STATIC_ASSERT_MSG(sizeof(T) == sizeof(int), "qi::Atomic is only supprted for int-like types");
77 
78  volatile
79 #ifdef _MSC_VER
80  long
81 #else
82  T
83 #endif
85  };
86 
87  template <typename T>
88  class Atomic: public AtomicBase<T>
89  {
90  public:
92  {
93  this->_value = 0;
94  }
95 
96  Atomic(T value)
97  {
98  this->_value = value;
99  }
100  };
101 #ifdef __GNUC__
102  template <typename T>
103  inline T AtomicBase<T>::operator++()
104  {
105  return __sync_add_and_fetch(&_value, 1);
106  }
107 
108  template <typename T>
109  inline T AtomicBase<T>::operator--()
110  {
111  return __sync_sub_and_fetch(&_value, 1);
112  }
113 
114  template <typename T>
115  inline AtomicBase<T>& AtomicBase<T>::operator=(T value)
116  {
117  __sync_lock_test_and_set(&_value, value);
118  return *this;
119  }
120 
121  template <typename T>
122  inline T AtomicBase<T>::swap(T value)
123  {
124  return __sync_lock_test_and_set(&_value, value);
125  }
126  template <typename T>
127  inline bool AtomicBase<T>::setIfEquals(T testValue, T setValue)
128  {
129  return __sync_bool_compare_and_swap(&_value, testValue, setValue);
130  }
131 #endif
132 
133 #ifdef _MSC_VER
134 
135  template <>
136  inline int AtomicBase<int>::operator++()
137  {
138  return _InterlockedIncrement(&_value);
139  }
140 
141  template <>
142  inline int AtomicBase<int>::operator--()
143  {
144  return _InterlockedDecrement(&_value);
145  }
146 
147  template<>
148  inline AtomicBase<int>& AtomicBase<int>::operator=(int value)
149  {
150  InterlockedExchange(&_value, value);
151  return *this;
152  }
153 
154  template<>
155  inline int AtomicBase<int>::swap(int value)
156  {
157  return InterlockedExchange(&_value, value);
158  }
159 
160  template <>
161  inline bool AtomicBase<int>::setIfEquals(int testValue, int setValue)
162  {
163  return _InterlockedCompareExchange(&_value, setValue, testValue) == testValue;
164  }
165 
166  template <>
167  inline unsigned int AtomicBase<unsigned int>::operator++()
168  {
169  return _InterlockedIncrement(&_value);
170  }
171 
172  template <>
173  inline unsigned int AtomicBase<unsigned int>::operator--()
174  {
175  return _InterlockedDecrement(&_value);
176  }
177 
178  template<>
179  inline AtomicBase<unsigned int>& AtomicBase<unsigned int>::operator=(unsigned int value)
180  {
181  InterlockedExchange(&_value, value);
182  return *this;
183  }
184 
185  template<>
186  inline unsigned int AtomicBase<unsigned int>::swap(unsigned int value)
187  {
188  return InterlockedExchange(&_value, value);
189  }
190 
191  template <>
192  inline bool AtomicBase<unsigned int>::setIfEquals(unsigned int testValue, unsigned int setValue)
193  {
194  return _InterlockedCompareExchange(&_value, setValue, testValue) == testValue;
195  }
196 #endif
197 
198 }
199 
200 #define _QI_INSTANCIATE(_, a, elem) ::qi::details::newAndAssign(&elem);
201 
202 /* The code below relies on the fact that initialisation of the qi::Atomic
203 * can happen at static initialization time, and that proper memory barriers
204 * are setup by its ++, swap and get operations.
205  */
210 #define QI_THREADSAFE_NEW(...) \
211  QI_ONCE(QI_VAARGS_APPLY(_QI_INSTANCIATE, _, __VA_ARGS__);)
212 
214 #define QI_ONCE(code) \
215  static qi::AtomicBase<int> QI_UNIQ_DEF(atomic_guard_a) = {0}; \
216  static qi::AtomicBase<int> QI_UNIQ_DEF(atomic_guard_b) = {0}; \
217  while (!QI_UNIQ_DEF(atomic_guard_a).setIfEquals(1, 1)) \
218  { \
219  bool tok = QI_UNIQ_DEF(atomic_guard_b).setIfEquals(0,1); \
220  if (tok) \
221  { \
222  code; \
223  ++QI_UNIQ_DEF(atomic_guard_a); \
224  } \
225  }
226 
227 
228 #endif // _QI_ATOMIC_HPP_
T operator*() const
Definition: atomic.hpp:70
volatile T _value
Definition: atomic.hpp:84
Atomic()
Default atomic constructor, setting value to 0.
Definition: atomic.hpp:91
Various macros for qi. (deprecated, export API, disallow copy, ..)
Atomic(T value)
Atomic constructor setting value to its parameter.
Definition: atomic.hpp:96
Atomic operations on integers.
Definition: atomic.hpp:88
long testAndSet(long *cond)
Definition: atomic.hpp:31
T swap(T value)
AtomicBase< T > & operator=(T value)
bool setIfEquals(T testValue, T setValue)
BOOST_STATIC_ASSERT_MSG(sizeof(T)==sizeof(int),"qi::Atomic is only supprted for int-like types")