library of assembled shared sources

http://lass.cocamware.com

atomic.h

Go to the documentation of this file.
00001 /** @file
00002  *  @author Bram de Greve (bramz@users.sourceforge.net)
00003  *  @author Tom De Muer (tomdemuer@users.sourceforge.net)
00004  *
00005  *  *** BEGIN LICENSE INFORMATION ***
00006  *  
00007  *  The contents of this file are subject to the Common Public Attribution License 
00008  *  Version 1.0 (the "License"); you may not use this file except in compliance with 
00009  *  the License. You may obtain a copy of the License at 
00010  *  http://lass.sourceforge.net/cpal-license. The License is based on the 
00011  *  Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover 
00012  *  use of software over a computer network and provide for limited attribution for 
00013  *  the Original Developer. In addition, Exhibit A has been modified to be consistent 
00014  *  with Exhibit B.
00015  *  
00016  *  Software distributed under the License is distributed on an "AS IS" basis, WITHOUT 
00017  *  WARRANTY OF ANY KIND, either express or implied. See the License for the specific 
00018  *  language governing rights and limitations under the License.
00019  *  
00020  *  The Original Code is LASS - Library of Assembled Shared Sources.
00021  *  
00022  *  The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
00023  *  The Original Developer is the Initial Developer.
00024  *  
00025  *  All portions of the code written by the Initial Developer are:
00026  *  Copyright (C) 2004-2007 the Initial Developer.
00027  *  All Rights Reserved.
00028  *  
00029  *  Contributor(s):
00030  *
00031  *  Alternatively, the contents of this file may be used under the terms of the 
00032  *  GNU General Public License Version 2 or later (the GPL), in which case the 
00033  *  provisions of GPL are applicable instead of those above.  If you wish to allow use
00034  *  of your version of this file only under the terms of the GPL and not to allow 
00035  *  others to use your version of this file under the CPAL, indicate your decision by 
00036  *  deleting the provisions above and replace them with the notice and other 
00037  *  provisions required by the GPL License. If you do not delete the provisions above,
00038  *  a recipient may use your version of this file under either the CPAL or the GPL.
00039  *  
00040  *  *** END LICENSE INFORMATION ***
00041  */
00042 
00043 #ifndef LASS_GUARDIAN_OF_INCLUSION_UTIL_ATOMIC_H
00044 #define LASS_GUARDIAN_OF_INCLUSION_UTIL_ATOMIC_H
00045 
00046 #include "util_common.h"
00047 #include "impl/atomic_impl.h"
00048 #include "../num/safe_bool.h"
00049 
00050 /** @defgroup atomic
00051  *  @brief atomic operations and tools for lock-free algorithms
00052  */
00053 
00054 namespace lass
00055 {
00056 namespace util
00057 {
00058 
00059 /** @ingroup atomic
00060  *  
00061  *  Performs the following pseudocode in an atomic way (no other threads can intervene):
00062  *  @code
00063  *      if (dest != expectedValue) return false;
00064  *      dest = newValue;
00065  *      return true;
00066  *  @endcode
00067  */
00068 template <typename T> inline 
00069 bool atomicCompareAndSwap(volatile T& dest, T expectedValue, T newValue)
00070 {
00071     return impl::AtomicOperations< sizeof(T) >::compareAndSwap(dest, expectedValue, newValue) 
00072         == expectedValue;
00073 }
00074 
00075 
00076 
00077 /** @ingroup atomic
00078  *  
00079  *  Performs the following pseudocode in an atomic way (no other threads can intervene):
00080  *  @code
00081  *      if (dest1 != expectedValue1 || *(T2*)(&dest1 + sizeof(T1)) != expected2) return false;
00082  *      dest1 = new1;
00083  *      dest2 = new2;
00084  *      return true;
00085  *  @endcode
00086  *
00087  *  Does not exist for 64-bit types (there's no 128-bit CAS).
00088  */
00089 template <typename T1, typename T2> inline 
00090 bool atomicCompareAndSwap(volatile T1& dest1, T1 expected1, T2 expected2, T1 new1, T2 new2)
00091 {
00092     LASS_META_ASSERT(sizeof(T1) == sizeof(T2), T1_and_T2_must_be_of_same_size);
00093     return impl::AtomicOperations< sizeof(T1) >::compareAndSwap(
00094         dest1, expected1, expected2, new1, new2);
00095 }
00096 
00097 
00098 
00099 /** @ingroup atomic
00100  *  
00101  *  Performs the following pseudocode in an atomic way (no other threads can intervene):
00102  *  @code
00103  *      ++value;
00104  *  @endcode
00105  */
00106 template <typename T> inline
00107 void atomicIncrement(volatile T& value)
00108 {
00109     impl::AtomicOperations< sizeof(T) >::increment(value);
00110 }
00111 
00112 
00113 
00114 /** @ingroup atomic
00115  *  
00116  *  Performs the following pseudocode in an atomic way (no other threads can intervene):
00117  *  @code
00118  *      --value;
00119  *  @endcode
00120  */
00121 template <typename T> inline
00122 void atomicDecrement(volatile T& value)
00123 {
00124     impl::AtomicOperations< sizeof(T) >::decrement(value);
00125 }
00126 
00127 
00128 
00129 #if LASS_COMPILER_TYPE == LASS_COMPILER_TYPE_MSVC
00130 #   pragma warning(push)
00131 #   pragma warning(disable: 4521) // multiple copy constructors specified
00132 #   pragma warning(disable: 4522) // multiple assignment operators specified
00133 #endif
00134 
00135 /** Pointer with a tag for ABA salvation
00136  *  @ingroup atomic
00137  *  Some lock-free algorithms suffer from the ABA problem when acting on pointers.
00138  *  This can be solved (read: be make very unlikely) by adding a tag to the pointer.
00139  */
00140 template <typename T>
00141 class TaggedPtr
00142 {
00143 public:
00144 #if LASS_ACTUAL_ADDRESS_SIZE == 48
00145     // We're in 64 bit space, but we don't have 128 bit CAS to do tagging ... Help!
00146     // Luckely, only the least significant 48 bits of a pointer are really used.
00147     // So, we have 16 bits we can fiddle with.  Should be enough for a counting tag.
00148     // Well, theoretically it isn't enough (no number of bits is ever enough to 
00149     // completely remove the possibility of a wrap-over, but at least it decreases
00150     // the odds somewhat ;)
00151     //
00152     // We store the the pointer in the most significant part for two reasons:
00153     //  - order of the pointers is somewhat preserved
00154     //  - we can use SAR to restore the sign bit.
00155     //
00156     typedef num::Tuint16 TTag;
00157     TaggedPtr(): bits_(0) {}
00158     TaggedPtr(T* ptr, TTag tag): bits_((reinterpret_cast<num::Tuint64>(ptr) << 16) | tag) {}
00159     TaggedPtr(const TaggedPtr& other): bits_(other.bits_) {}
00160     TaggedPtr(const volatile TaggedPtr& other): bits_(other.bits_) {}
00161     TaggedPtr& operator=(const TaggedPtr& other) { bits_ = other.bits_; return *this; }
00162     TaggedPtr& operator=(const volatile TaggedPtr& other) { bits_ = other.bits_; return *this; }
00163     T* const get() const
00164     {
00165 #   if defined(LASS_HAVE_INLINE_ASSEMBLY_GCC)
00166         T* ptr;
00167         __asm__ __volatile__("sarq $16, %0;" : "=q"(ptr) : "0"(bits_) : "cc");
00168         return ptr;
00169 #   elif defined(LASS_UTIL_IMPL_ATOMIC_MSVC_X64)
00170         return reinterpret_cast<T*>(__ll_rshift(*reinterpret_cast<const volatile __int64*>(&bits_), 16));
00171 #   else
00172         return (bits_ & 0xa000000000000000 == 0) ?
00173             reinterpret_cast<T*>(bits_ >> 16) :
00174             reinterpret_cast<T*>((bits_ >> 16) | 0xffff000000000000);
00175 #   endif
00176     }
00177     const TTag tag() const { return static_cast<TTag>(bits_ & 0xffff); }
00178     const bool operator==(const TaggedPtr& other) const { return bits_ == other.bits_; }
00179     const bool operator==(const volatile TaggedPtr& other) const { return bits_ == other.bits_; }
00180     bool atomicCompareAndSwap(const TaggedPtr& expected, const TaggedPtr& fresh) volatile
00181     {
00182         return util::atomicCompareAndSwap(bits_, expected.bits_, fresh.bits_);
00183     }
00184 private:
00185     num::Tuint64 bits_;
00186 #elif LASS_ACTUAL_ADDRESS_SIZE == 32
00187     // We're in 32 bit space, so we use a 64 bit CAS to do tagging
00188     //
00189     typedef num::TuintPtr TTag;
00190     TaggedPtr(): ptr_(0), tag_(0) {}
00191     TaggedPtr(T* ptr, TTag tag): ptr_(ptr), tag_(tag) {}
00192     TaggedPtr(const TaggedPtr& other): ptr_(other.ptr_), tag_(other.tag_) {}
00193     TaggedPtr(const volatile TaggedPtr& other): ptr_(other.ptr_), tag_(other.tag_) {}
00194     TaggedPtr& operator=(const TaggedPtr& other) { ptr_ = other.ptr_; tag_ = other.tag_; return *this; }
00195     TaggedPtr& operator=(const volatile TaggedPtr& other) { ptr_ = other.ptr_; tag_ = other.tag_; return *this; }
00196     T* const get() const { return ptr_; }
00197     const TTag tag() const { return tag_; }
00198     bool operator==(const TaggedPtr& other) const { return ptr_ == other.ptr_ && tag_ == other.tag_; }
00199     bool operator==(const volatile TaggedPtr& other) const { return ptr_ == other.ptr_ && tag_ == other.tag_; }
00200     bool atomicCompareAndSwap(const TaggedPtr& expected, const TaggedPtr& fresh) volatile
00201     {
00202         return util::atomicCompareAndSwap(
00203             ptr_, expected.ptr_, expected.tag_, fresh.ptr_, fresh.tag_);
00204     }
00205 private:
00206     T* ptr_;
00207     TTag tag_;
00208 #else
00209 #   error "not implemented yet [Bramz]"
00210 #endif
00211 public:
00212     T* const operator->() const { LASS_ASSERT(get()); return get(); }
00213     const bool operator!() const { return get() == 0; }
00214     operator num::SafeBool() const { return get() ? num::safeTrue : num::safeFalse; }
00215 };
00216 
00217 #if LASS_COMPILER_TYPE == LASS_COMPILER_TYPE_MSVC
00218 #   pragma warning(pop)
00219 #endif
00220 
00221 }
00222 }
00223 
00224 #endif
00225 
00226 // EOF

Generated on Mon Nov 10 14:19:59 2008 for Library of Assembled Shared Sources by doxygen 1.5.7.1
SourceForge.net Logo