threadSafeRefCount.h
Engine/source/platform/threads/threadSafeRefCount.h
Templated code for concurrent reference-counting.
Classes:
class
Reference to a concurrently reference-counted object.
class
Baseclass for concurrently reference-counted objects.
Public Functions
T &
Deref(const ThreadSafeRef< T > & ref)
T &
Deref(ThreadSafeRef< T > & ref)
Detailed Description
Templated code for concurrent reference-counting.
Part of this code is based on work by J.D. Valois, Michael M. Maged, and Scott L. Michael.
Public Functions
Deref(const ThreadSafeRef< T > & ref)
Deref(ThreadSafeRef< T > & ref)
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#ifndef _THREADSAFEREFCOUNT_H_ 25#define _THREADSAFEREFCOUNT_H_ 26 27#ifndef _PLATFORMINTRINSICS_H_ 28# include "platform/platformIntrinsics.h" 29#endif 30#ifndef _TYPETRAITS_H_ 31# include "platform/typetraits.h" 32#endif 33 34 35/// @file 36/// Templated code for concurrent reference-counting. 37/// 38/// Part of this code is based on work by J.D. Valois, Michael M. Maged, 39/// and Scott L. Michael. 40 41 42//-------------------------------------------------------------------------- 43// ThreadSafeRefCount. 44//-------------------------------------------------------------------------- 45 46/// Baseclass for concurrently reference-counted objects. 47/// 48/// @note NOTE that freshly instantiated objects start out with a reference 49/// count of ZERO! Depending on how this class is used, this may not 50/// be desirable, so override this behavior in constructors if necessary. 51/// 52/// @param T the class being reference counted; this is passed to this class, 53/// so it can call the correct destructor without having to force users 54/// to have virtual methods 55 56template< class T, class DeletePolicy = DeleteSingle > 57class ThreadSafeRefCount 58{ 59 public: 60 61 typedef void Parent; 62 63 ThreadSafeRefCount() 64 : mRefCount( 0 ) {} 65 ThreadSafeRefCount( bool noSet ) {} 66 67 bool isShared() const; 68 U32 getRefCount() const; 69 void addRef(); 70 void release(); 71 void clearLowestBit(); 72 static T* safeRead( T* const volatile& refPtr ); 73 74 protected: 75 76 U32 mRefCount; ///< Reference count and claim bit. Note that this increments in steps of two. 77 78 static U32 decrementAndTestAndSet( U32& refCount ); 79}; 80 81/// @return true if the object is referenced by more than a single 82/// reference. 83 84template< class T, class DeletePolicy > 85inline bool ThreadSafeRefCount< T, DeletePolicy >::isShared() const 86{ 87 return ( mRefCount > 3 ); 88} 89 90/// Get the current reference count. This method is mostly meant for 91/// debugging and should not normally be used. 92 93template< class T, class DeletePolicy > 94inline U32 ThreadSafeRefCount< T, DeletePolicy >::getRefCount() const 95{ 96 return mRefCount; 97} 98 99/// Increase the reference count on the object. 100 101template< class T, class DeletePolicy > 102inline void ThreadSafeRefCount< T, DeletePolicy >::addRef() 103{ 104 dFetchAndAdd( mRefCount, 2 ); 105} 106 107/// Decrease the object's reference count and delete the object, if the count 108/// drops to zero and claiming the object by the current thread succeeds. 109 110template< class T, class DeletePolicy > 111inline void ThreadSafeRefCount< T, DeletePolicy >::release() 112{ 113 AssertFatal( mRefCount != 0, "ThreadSafeRefCount::release() - refcount of zero" ); 114 if( decrementAndTestAndSet( mRefCount ) != 0 ) 115 DeletePolicy::destroy( ( T* ) this ); 116} 117 118/// Dereference a reference-counted pointer in a multi-thread safe way. 119 120template< class T, class DeletePolicy > 121T* ThreadSafeRefCount< T, DeletePolicy >::safeRead( T* const volatile& refPtr ) 122{ 123 while( 1 ) 124 { 125 // Support tagged pointers here. 126 127 T* ptr = TypeTraits< T* >::getUntaggedPtr( refPtr ); 128 if( !ptr ) 129 return 0; 130 131 ptr->addRef(); 132 if( ptr == TypeTraits< T* >::getUntaggedPtr( refPtr ) ) 133 return ptr; 134 else 135 ptr->release(); 136 } 137} 138 139/// Decrement the given reference count. Return 1 if the count dropped to zero 140/// and the claim bit has been successfully set; return 0 otherwise. 141 142template< class T, class DeletePolicy > 143U32 ThreadSafeRefCount< T, DeletePolicy >::decrementAndTestAndSet( U32& refCount ) 144{ 145 U32 oldVal; 146 U32 newVal; 147 148 do 149 { 150 oldVal = refCount; 151 newVal = oldVal - 2; 152 153 AssertFatal( oldVal >= 2, 154 "ThreadSafeRefCount::decrementAndTestAndSet() - invalid refcount" ); 155 156 if( newVal == 0 ) 157 newVal = 1; 158 } 159 while( !dCompareAndSwap( refCount, oldVal, newVal ) ); 160 161 return ( ( oldVal - newVal ) & 1 ); 162} 163 164/// 165 166template< class T, class DeletePolicy > 167inline void ThreadSafeRefCount< T, DeletePolicy >::clearLowestBit() 168{ 169 AssertFatal( mRefCount % 2 != 0, "ThreadSafeRefCount::clearLowestBit() - invalid refcount" ); 170 171 U32 oldVal; 172 U32 newVal; 173 174 do 175 { 176 oldVal = mRefCount; 177 newVal = oldVal - 1; 178 } 179 while( !dCompareAndSwap( mRefCount, oldVal, newVal ) ); 180} 181 182//-------------------------------------------------------------------------- 183// ThreadSafeRef. 184//-------------------------------------------------------------------------- 185 186/// Reference to a concurrently reference-counted object. 187/// 188/// This class takes care of the reference-counting as well as protecting 189/// the reference itself from concurrent operations. 190/// 191/// Tagging allows the pointer contained in the reference to be flagged. 192/// Tag state is preserved through updates to the reference. 193/// 194/// @note If you directly assign a freshly created object with a reference 195/// count of zero to a ThreadSafeRef, make absolutely sure the ThreadSafeRef 196/// is accessed only by a single thread. Otherwise there's a risk of the 197/// object being released and freed in midst of trying to set the reference. 198template< class T > 199class ThreadSafeRef 200{ 201 public: 202 203 enum ETag 204 { 205 TAG_PreserveOld, ///< Preserve existing tagging state when changing pointer. 206 TAG_PreserveNew, ///< Preserve tagging state of new pointer when changing pointer. 207 TAG_Set, ///< Set tag when changing pointer; okay if already set. 208 TAG_Unset, ///< Unset tag when changing pointer; okay if already unset. 209 TAG_SetOrFail, ///< Set tag when changing pointer; fail if already set. 210 TAG_UnsetOrFail, ///< Unset tag when changing pointer; fail if already unset. 211 TAG_FailIfSet, ///< Fail changing pointer when currently tagged. 212 TAG_FailIfUnset ///< Fail changing pointer when currently untagged. 213 }; 214 215 typedef ThreadSafeRef< T> ThisType; 216 217 ThreadSafeRef() : mPtr( 0 ) {} 218 ThreadSafeRef( T* ptr ) : mPtr( ThreadSafeRefCount< T >::safeRead( ptr ) ) {} 219 ThreadSafeRef( const ThisType& ref ) : mPtr( ThreadSafeRefCount< T >::safeRead( ref.mPtr ) ) {} 220 ~ThreadSafeRef() 221 { 222 T* ptr = NULL; 223 while( !trySetFromTo( mPtr, ptr ) ); 224 } 225 226 T* ptr() const { return getUntaggedPtr( mPtr ) ; } 227 void setTag() { while( !trySetFromTo( mPtr, mPtr, TAG_Set ) ); } 228 bool isTagged() const { return isTaggedPtr( mPtr ); } 229 bool trySetFromTo( T* oldVal, T* const volatile& newVal, ETag tag = TAG_PreserveOld ); 230 bool trySetFromTo( T* oldVal, const ThisType& newVal, ETag tag = TAG_PreserveOld ); 231 bool trySetFromTo( const ThisType& oldVal, const ThisType& newVal, ETag tag = TAG_PreserveOld ); 232 static void unsafeWrite( ThisType& ref, T* ptr ); 233 static T* safeRead( T* const volatile& refPtr ) { return ThreadSafeRefCount< T >::safeRead( refPtr ); } 234 235 bool operator==( T* ptr ) const; 236 bool operator==( const ThisType& ref ) const; 237 bool operator!=( T* ptr ) const { return !( *this == ptr ); } 238 bool operator!=( const ThisType& ref ) const { return !( *this == ref ); } 239 ThisType& operator=( T* ptr ); 240 ThisType& operator=( const ThisType& ref ); 241 242 bool operator!() const { return ( ptr() == 0 ); } 243 T& operator*() const { return *ptr(); } 244 T* operator->() const { return ptr(); } 245 operator T*() const { return ptr(); } 246 247 protected: 248 249 T* volatile mPtr; 250 251 static bool isTaggedPtr( T* ptr ) { return TypeTraits< T* >::isTaggedPtr( ptr ); } 252 static T* getTaggedPtr( T* ptr ) { return TypeTraits< T* >::getTaggedPtr( ptr ); } 253 static T* getUntaggedPtr( T* ptr ) { return TypeTraits< T* >::getUntaggedPtr( ptr ); } 254}; 255 256/// Update the reference from pointing to oldVal to point to newVal. 257/// Do so in a thread-safe way. 258/// 259/// This operation will only succeed, if, when doing the pointer-swapping, 260/// the reference still points to oldVal. If, however, the reference 261/// has been changed in the meantime by another thread, the operation will 262/// fail. 263/// 264/// @param oldVal The pointer assumed to currently be contained in this ThreadSafeRef. 265/// @param newVal The pointer to store in this ThreadSafeRef. 266/// @param tag Operation to perform on the reference's tag field. 267/// 268/// @return true, if the reference now points to newVal. 269 270template< class T > 271bool ThreadSafeRef< T >::trySetFromTo( T* oldVal, T* const volatile& newVal, ETag tag ) 272{ 273 bool setTag = false; 274 bool getTag = false; 275 bool isTagged = isTaggedPtr( oldVal ); 276 277 switch( tag ) 278 { 279 case TAG_PreserveOld: setTag = isTaggedPtr( oldVal ); break; 280 case TAG_PreserveNew: setTag = isTaggedPtr( newVal ); break; 281 case TAG_Set: setTag = true; break; 282 case TAG_Unset: setTag = false; break; 283 case TAG_SetOrFail: setTag = true; getTag = true; break; 284 case TAG_UnsetOrFail: setTag = false; getTag = true; break; 285 case TAG_FailIfSet: if( isTagged ) return false; break; 286 case TAG_FailIfUnset: if( !isTagged ) return false; break; 287 } 288 289 T* newValPtr = ( setTag 290 ? getTaggedPtr( ThreadSafeRefCount< T >::safeRead( newVal ) ) 291 : getUntaggedPtr( ThreadSafeRefCount< T >::safeRead( newVal ) ) ); 292 293 if( dCompareAndSwap( mPtr, 294 ( getTag 295 ? ( setTag 296 ? getUntaggedPtr( oldVal ) 297 : getTaggedPtr( oldVal ) ) 298 : oldVal ), 299 newValPtr ) ) 300 { 301 if( getUntaggedPtr( oldVal ) ) 302 getUntaggedPtr( oldVal )->release(); 303 return true; 304 } 305 else 306 { 307 if( getUntaggedPtr( newValPtr ) ) 308 getUntaggedPtr( newValPtr )->release(); 309 return false; 310 } 311} 312 313template< class T > 314inline bool ThreadSafeRef< T >::trySetFromTo( T* oldVal, const ThisType& newVal, ETag tag ) 315{ 316 return trySetFromTo( oldVal, newVal.mPtr, tag ); 317} 318 319template< class T > 320inline bool ThreadSafeRef< T >::trySetFromTo( const ThisType& oldVal, const ThisType& newVal, ETag tag ) 321{ 322 return trySetFromTo( oldVal.mPtr, newVal.mPtr, tag ); 323} 324 325/// Update ref to point to ptr but <em>do not</em> release an existing 326/// reference held by ref nor do the operation in a thread-safe way. 327/// 328/// This method is <em>only</em> for when you absolutely know that your 329/// thread is the only thread operating on a reference <em>and</em> you 330/// are keeping track of reference counts yourself. 331/// 332/// @param ref The reference to update. 333/// @param ptr The new pointer to store in ref. 334 335template< class T > 336inline void ThreadSafeRef< T >::unsafeWrite( ThisType& ref, T* ptr ) 337{ 338 ref.mPtr = ptr; 339} 340 341template< class T > 342inline bool ThreadSafeRef< T >::operator==( T* p ) const 343{ 344 return ( ptr() == p ); 345} 346 347template< class T > 348inline bool ThreadSafeRef< T >::operator==( const ThisType& ref ) const 349{ 350 return ( ptr() == ref.ptr() ); 351} 352 353template< class T > 354inline ThreadSafeRef< T>& ThreadSafeRef< T >::operator=( T* ptr ) 355{ 356 while( !trySetFromTo( mPtr, ptr, TAG_PreserveNew ) ); 357 return *this; 358} 359 360template< class T > 361inline ThreadSafeRef< T>& ThreadSafeRef< T >::operator=( const ThisType& ref ) 362{ 363 while( !trySetFromTo( mPtr, ref, TAG_PreserveNew ) ); 364 return *this; 365} 366 367 368template< typename T > 369struct TypeTraits< ThreadSafeRef< T > > : public TypeTraits< T* > {}; 370template< typename T > 371inline T& Deref( ThreadSafeRef< T>& ref ) 372{ 373 return *ref; 374} 375template< typename T > 376inline T& Deref( const ThreadSafeRef< T>& ref ) 377{ 378 return *ref; 379} 380 381#endif // _THREADSAFEREFCOUNT_H_ 382