libqi-api
2.0.6.8
|
00001 #pragma once 00002 /* 00003 * Copyright (c) 2012 Aldebaran Robotics. All rights reserved. 00004 * Use of this source code is governed by a BSD-style license that can be 00005 * found in the COPYING file. 00006 */ 00007 00008 00009 #ifndef _QI_ATOMIC_HPP_ 00010 #define _QI_ATOMIC_HPP_ 00011 00012 #ifdef _MSC_VER 00013 # include <windows.h> 00014 # include <intrin.h> 00015 00016 extern "C" long __cdecl _InterlockedIncrement(long volatile *); 00017 extern "C" long __cdecl _InterlockedDecrement(long volatile *); 00018 00019 # pragma intrinsic(_InterlockedIncrement) 00020 # pragma intrinsic(_InterlockedDecrement) 00021 00022 #endif 00023 00024 #include <boost/static_assert.hpp> 00025 00026 #include <qi/config.hpp> 00027 #include <qi/macro.hpp> 00028 00029 namespace qi 00030 { 00031 inline long testAndSet(long* cond) 00032 { 00033 #if defined __GNUC__ 00034 return __sync_bool_compare_and_swap(cond, 0, 1); 00035 #elif defined _MSC_VER 00036 return 1 - InterlockedCompareExchange(cond, 1, 0); 00037 #else 00038 #error "Unknown platform, testAndSet not implemented" 00039 #endif 00040 } 00041 00042 /* /!\ WARNING 00043 * The 'volatile' is needed even though we use atomic compiler builtins. 00044 * Without the volatile, a thread doing 00045 * while (!setIfEquals(1,1)) 00046 * Is never unstuck by a thread doing 00047 * setIfEquals(0,1) 00048 * 00049 * AtomicBase has public member so that it can be initialized at 00050 * static-initialization time (to make thread-safe static initialization inside 00051 * functions) 00052 */ 00053 template <typename T> 00054 struct AtomicBase 00055 { 00056 public: 00057 00058 00059 /* prefix operators */ 00060 inline T operator++(); 00061 inline T operator--(); 00062 inline AtomicBase<T>& operator=(T value); 00066 inline bool setIfEquals(T testValue, T setValue); 00067 00068 inline T swap(T value); 00069 00070 inline T operator*() const 00071 { 00072 return _value; 00073 } 00074 00075 public: 00076 BOOST_STATIC_ASSERT_MSG(sizeof(T) == sizeof(int), "qi::Atomic is only supprted for int-like types"); 00077 00078 volatile 00079 #ifdef _MSC_VER 00080 long 00081 #else 00082 T 00083 #endif 00084 _value; 00085 }; 00086 00087 template <typename T> 00088 class Atomic: public AtomicBase<T> 00089 { 00090 public: 00091 Atomic() 00092 { 00093 this->_value = 0; 00094 } 00095 00096 Atomic(T value) 00097 { 00098 this->_value = value; 00099 } 00100 }; 00101 #ifdef __GNUC__ 00102 template <typename T> 00103 inline T AtomicBase<T>::operator++() 00104 { 00105 return __sync_add_and_fetch(&_value, 1); 00106 } 00107 00108 template <typename T> 00109 inline T AtomicBase<T>::operator--() 00110 { 00111 return __sync_sub_and_fetch(&_value, 1); 00112 } 00113 00114 template <typename T> 00115 inline AtomicBase<T>& AtomicBase<T>::operator=(T value) 00116 { 00117 __sync_lock_test_and_set(&_value, value); 00118 return *this; 00119 } 00120 00121 template <typename T> 00122 inline T AtomicBase<T>::swap(T value) 00123 { 00124 return __sync_lock_test_and_set(&_value, value); 00125 } 00126 template <typename T> 00127 inline bool AtomicBase<T>::setIfEquals(T testValue, T setValue) 00128 { 00129 return __sync_bool_compare_and_swap(&_value, testValue, setValue); 00130 } 00131 #endif 00132 00133 #ifdef _MSC_VER 00134 00135 template <> 00136 inline int AtomicBase<int>::operator++() 00137 { 00138 return _InterlockedIncrement(&_value); 00139 } 00140 00141 template <> 00142 inline int AtomicBase<int>::operator--() 00143 { 00144 return _InterlockedDecrement(&_value); 00145 } 00146 00147 template<> 00148 inline AtomicBase<int>& AtomicBase<int>::operator=(int value) 00149 { 00150 InterlockedExchange(&_value, value); 00151 return *this; 00152 } 00153 00154 template<> 00155 inline int AtomicBase<int>::swap(int value) 00156 { 00157 return InterlockedExchange(&_value, value); 00158 } 00159 00160 template <> 00161 inline bool AtomicBase<int>::setIfEquals(int testValue, int setValue) 00162 { 00163 return _InterlockedCompareExchange(&_value, setValue, testValue) == testValue; 00164 } 00165 00166 template <> 00167 inline unsigned int AtomicBase<unsigned int>::operator++() 00168 { 00169 return _InterlockedIncrement(&_value); 00170 } 00171 00172 template <> 00173 inline unsigned int AtomicBase<unsigned int>::operator--() 00174 { 00175 return _InterlockedDecrement(&_value); 00176 } 00177 00178 template<> 00179 inline AtomicBase<unsigned int>& AtomicBase<unsigned int>::operator=(unsigned int value) 00180 { 00181 InterlockedExchange(&_value, value); 00182 return *this; 00183 } 00184 00185 template<> 00186 inline unsigned int AtomicBase<unsigned int>::swap(unsigned int value) 00187 { 00188 return InterlockedExchange(&_value, value); 00189 } 00190 00191 template <> 00192 inline bool AtomicBase<unsigned int>::setIfEquals(unsigned int testValue, unsigned int setValue) 00193 { 00194 return _InterlockedCompareExchange(&_value, setValue, testValue) == testValue; 00195 } 00196 #endif 00197 00198 } 00199 00200 #define _QI_INSTANCIATE(_, a, elem) ::qi::details::newAndAssign(&elem); 00201 00202 /* The code below relies on the fact that initialisation of the qi::Atomic 00203 * can happen at static initialization time, and that proper memory barriers 00204 * are setup by its ++, swap and get operations. 00205 */ 00210 #define QI_THREADSAFE_NEW(...) \ 00211 QI_ONCE(QI_VAARGS_APPLY(_QI_INSTANCIATE, _, __VA_ARGS__);) 00212 00214 #define QI_ONCE(code) \ 00215 static qi::AtomicBase<int> QI_UNIQ_DEF(atomic_guard_a) = {0}; \ 00216 static qi::AtomicBase<int> QI_UNIQ_DEF(atomic_guard_b) = {0}; \ 00217 while (!QI_UNIQ_DEF(atomic_guard_a).setIfEquals(1, 1)) \ 00218 { \ 00219 bool tok = QI_UNIQ_DEF(atomic_guard_b).setIfEquals(0,1); \ 00220 if (tok) \ 00221 { \ 00222 code; \ 00223 ++QI_UNIQ_DEF(atomic_guard_a); \ 00224 } \ 00225 } 00226 00227 00228 #endif // _QI_ATOMIC_HPP_