libqi-api  2.0.6.8
/home/opennao/work/master/sdk/libqi/qi/atomic.hpp
Go to the documentation of this file.
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_
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines