libqi-api  2.8.7.4
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros 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 #ifndef QI_ATOMIC_HPP_
9 #define QI_ATOMIC_HPP_
10 
11 #include <boost/predef.h>
12 
13 
14 #if BOOST_OS_WINDOWS
15 // We need to "scope" our usage of windows.h to avoid
16 // clients to end up with issues at compile time.
17 // TODO: remove all this when we switch totally to std::atomic
18 # pragma push_macro("NOMINMAX")
19 # pragma push_macro("WIN32_LEAN_AND_MEAN")
20 #
21 # ifndef NOMINMAX
22 # define NOMINMAX // Deactivates min/max macros from windows.h
23 # endif
24 #
25 # ifndef WIN32_LEAN_AND_MEAN
26 # define WIN32_LEAN_AND_MEAN // Deactivates unnecessary parts of windows.h
27 # endif
28 #
29 # include <windows.h>
30 # include <intrin.h>
31 #
32 # pragma pop_macro("WIN32_LEAN_AND_MEAN")
33 # pragma pop_macro("NOMINMAX")
34 #endif
35 
36 
37 #include <atomic>
38 #include <qi/config.hpp>
39 #include <qi/macro.hpp>
40 
41 namespace qi
42 {
43 
48 inline long testAndSet(long* cond)
49 {
50 #if defined __GNUC__
51  return __sync_bool_compare_and_swap(cond, 0, 1);
52 #elif defined _MSC_VER
53  return 1 - InterlockedCompareExchange(cond, 1, 0);
54 #else
55  #error "Unknown platform, testAndSet not implemented"
56 #endif
57 }
58 
59 namespace detail
60 {
61 /* /!\ WARNING
62  * The 'volatile' is needed even though we use atomic compiler builtins.
63  * Without the volatile, a thread doing
64  * while (!setIfEquals(1,1))
65  * Is never unstuck by a thread doing
66  * setIfEquals(0,1)
67  *
68  * StaticAtomicInt has public member so that it can be initialized at
69  * static-initialization time (to make thread-safe static initialization
70  * inside functions)
71  */
73 {
74 public:
75  /* prefix operators */
76  inline int operator++();
77  inline int operator--();
78  inline StaticAtomicInt& operator=(int value);
79  inline bool setIfEquals(int testValue, int setValue);
80 
81  inline int swap(int value);
82 
83  inline int operator*() const
84  {
85  return _value;
86  }
87 
88 public:
89  volatile
90 #ifdef _MSC_VER
91  long
92 #else
93  int
94 #endif
96 };
97 
98 #ifdef __GNUC__
99 inline int StaticAtomicInt::operator++()
100 {
101  return __sync_add_and_fetch(&_value, 1);
102 }
103 inline int StaticAtomicInt::operator--()
104 {
105  return __sync_sub_and_fetch(&_value, 1);
106 }
107 inline StaticAtomicInt& StaticAtomicInt::operator=(int value)
108 {
109  __sync_lock_test_and_set(&_value, value);
110  return *this;
111 }
112 inline int StaticAtomicInt::swap(int value)
113 {
114  return __sync_lock_test_and_set(&_value, value);
115 }
116 inline bool StaticAtomicInt::setIfEquals(int testValue, int setValue)
117 {
118  return __sync_bool_compare_and_swap(&_value, testValue, setValue);
119 }
120 #elif defined(_MSC_VER)
121 inline int StaticAtomicInt::operator++()
122 {
123  return _InterlockedIncrement(&_value);
124 }
125 inline int StaticAtomicInt::operator--()
126 {
127  return _InterlockedDecrement(&_value);
128 }
129 inline StaticAtomicInt& StaticAtomicInt::operator=(int value)
130 {
131  InterlockedExchange(&_value, value);
132  return *this;
133 }
134 inline int StaticAtomicInt::swap(int value)
135 {
136  return InterlockedExchange(&_value, value);
137 }
138 inline bool StaticAtomicInt::setIfEquals(int testValue, int setValue)
139 {
140  return _InterlockedCompareExchange(&_value, setValue, testValue) == testValue;
141 }
142 #endif
143 }
144 
155 template <typename T>
156 struct Atomic
157 {
158  std::atomic<T> _value;
159 public:
160  /* Default atomic constructor, setting value to 0.
161  */
163  : _value{}
164  {}
168  Atomic(T value)
169  : _value(std::move(value))
170  {}
171  // This is needed in c++03 for lines like:
172  // Atomic<int> i = 0;
173  // There is no copy there, but the constructor *must* exist
174  Atomic(const Atomic& other)
175  : _value(other._value.load())
176  {}
177 
180  { return ++_value; }
183  { return --_value; }
184 
186  T operator++(int)
187  {
188  return _value++;
189  }
191  T operator--(int)
192  {
193  return _value--;
194  }
195 
197  { _value = std::move(value); return *this; }
199  { _value = value.load(); return *this; }
200 
204  bool setIfEquals(T testValue, T setValue)
205  { return _value.compare_exchange_strong(testValue, setValue); }
206 
210  T swap(T value)
211  { return _value.exchange(value); }
212 
217  QI_API_DEPRECATED_MSG(Use 'load' instead)
218  T operator*() const
219  { return _value.load(); }
220 
221  T load() const
222  { return _value.load(); }
223 };
224 
225 namespace detail
226 {
227 template<typename T> void newAndAssign(T** ptr)
228 {
229  *ptr = new T();
230 }
231 } // namespace detail
232 
238 inline bool tryRaiseAtomicFlag(std::atomic<bool>& b)
239 {
240  bool expected = false;
241  const bool desired = true;
242  return b.compare_exchange_strong(expected, desired);
243 }
244 
251 inline bool tryLowerAtomicFlag(std::atomic<bool>& b)
252 {
253  bool expected = true;
254  const bool desired = false;
255  return b.compare_exchange_strong(expected, desired);
256 }
257 
259 {
260  std::atomic_flag* _flag = nullptr;
261  bool _locked = false;
262 
263  void cleanup() QI_NOEXCEPT(true)
264  {
265  if (_flag && _locked)
266  {
267  _flag->clear();
268  _locked = false;
269  }
270  }
271 
272 public:
273  explicit AtomicFlagLock(std::atomic_flag& f)
274  : _flag{ &f }
275  , _locked{ !f.test_and_set() } // locked if the flag was not already set
276  {}
277 
278  AtomicFlagLock(const AtomicFlagLock&) = delete;
279  AtomicFlagLock& operator=(const AtomicFlagLock&) = delete;
280 
282  : _flag{ o._flag }
283  , _locked{ o._locked }
284  {
285  o._flag = nullptr;
286  o._locked = false;
287  }
288 
290  {
291  cleanup();
292 
293  _flag = o._flag;
294  _locked = o._locked;
295  o._flag = nullptr;
296  o._locked = false;
297  return *this;
298  }
299 
301  {
302  cleanup();
303  }
304 
305  explicit operator bool() const
306  {
307  return _locked;
308  }
309 };
310 
312 inline AtomicFlagLock scopelock(std::atomic_flag& f)
313 {
314  return AtomicFlagLock{ f };
315 }
316 
317 template<typename T>
318 T src(const std::atomic<T>& x)
319 {
320  return x.load();
321 }
322 
323 } // namespace qi
324 
325 #define _QI_INSTANCIATE(_, a, elem) ::qi::detail::newAndAssign(&elem);
326 
327 /* The code below relies on the fact that initialisation of the qi::Atomic
328  * can happen at static initialization time, and that proper memory barriers
329  * are setup by its ++, swap and get operations.
330  */
331 
374 #define QI_THREADSAFE_NEW(...) \
375  QI_ONCE(QI_VAARGS_APPLY(_QI_INSTANCIATE, _, __VA_ARGS__);)
376 
420 #define QI_ONCE(code) \
421  static qi::detail::StaticAtomicInt QI_UNIQ_DEF(atomic_guard_a) = {0}; \
422  static qi::detail::StaticAtomicInt QI_UNIQ_DEF(atomic_guard_b) = {0}; \
423  while (!QI_UNIQ_DEF(atomic_guard_a).setIfEquals(1, 1)) \
424  { \
425  bool tok = QI_UNIQ_DEF(atomic_guard_b).setIfEquals(0, 1); \
426  if (tok) \
427  { \
428  try \
429  { \
430  code; \
431  } \
432  catch (...) \
433  { \
434  QI_UNIQ_DEF(atomic_guard_b) = 0; \
435  throw; \
436  } \
437  ++QI_UNIQ_DEF(atomic_guard_a); \
438  } \
439  }
440 
441 #endif // QI_ATOMIC_HPP_
void setValue(qi::Promise< R > &p, const boost::function< R()> &f)
Atomic(T value)
Definition: atomic.hpp:168
T swap(T value)
Definition: atomic.hpp:210
T operator--()
Atomic pre-decrement of the value.
Definition: atomic.hpp:182
bool setIfEquals(T testValue, T setValue)
Definition: atomic.hpp:204
bool setIfEquals(int testValue, int setValue)
int operator*() const
Definition: atomic.hpp:83
Atomic< T > & operator=(T value)
Definition: atomic.hpp:196
StaticAtomicInt & operator=(int value)
bool tryLowerAtomicFlag(std::atomic< bool > &b)
Definition: atomic.hpp:251
AtomicFlagLock(std::atomic_flag &f)
Definition: atomic.hpp:273
long testAndSet(long *cond)
Definition: atomic.hpp:48
bool tryRaiseAtomicFlag(std::atomic< bool > &b)
Definition: atomic.hpp:238
#define QI_NOEXCEPT(cond)
Specify that a function may throw or not. Do nothing if noexcept is not available.
Definition: macro.hpp:318
Atomic(const Atomic &other)
Definition: atomic.hpp:174
void newAndAssign(T **ptr)
Definition: atomic.hpp:227
T operator++()
Atomic pre-increment of the value.
Definition: atomic.hpp:179
T operator++(int)
Atomic post-increment of the value.
Definition: atomic.hpp:186
#define QI_API_DEPRECATED_MSG(msg__)
Compiler flags to mark a function as deprecated. It will generate a compiler warning.
Definition: macro.hpp:55
std::atomic< T > _value
Definition: atomic.hpp:158
Atomic< T > & operator=(const Atomic< T > &value)
Definition: atomic.hpp:198
T operator--(int)
Atomic post-decrement of the value.
Definition: atomic.hpp:191
AtomicFlagLock(AtomicFlagLock &&o)
Definition: atomic.hpp:281
Various macros for qi. (deprecated, export API, disallow copy, ..) <includename>qi/macro.hpp</includename> .
AtomicFlagLock & operator=(AtomicFlagLock &&o)
Definition: atomic.hpp:289
AtomicFlagLock & operator=(const AtomicFlagLock &)=delete
T src(const std::atomic< T > &x)
Definition: atomic.hpp:318
AtomicFlagLock scopelock(std::atomic_flag &f)
model ScopeLockable std::atomic_flag:
Definition: atomic.hpp:312
T load() const
Definition: atomic.hpp:221