2
0

threadSafeRefCount.h 13 KB

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