shared_ptr_mt.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. ///////////////////////////////////////////////////////////////////////////////
  5. // This is a multithread-safe version of shared_ptr_mt.
  6. // For basic documentation, see shared_ptr_mt.
  7. ///////////////////////////////////////////////////////////////////////////////
  8. #ifndef EATHREAD_SHARED_PTR_MT_H
  9. #define EATHREAD_SHARED_PTR_MT_H
  10. #ifndef INCLUDED_eabase_H
  11. #include <EABase/eabase.h>
  12. #endif
  13. #ifndef EATHREAD_EATHREAD_FUTEX_H
  14. #include <eathread/eathread_futex.h>
  15. #endif
  16. #ifndef EATHREAD_EATHREAD_ATOMIC_H
  17. #include <eathread/eathread_atomic.h>
  18. #endif
  19. // #include <memory> Temporarily disabled while we wait for compilers to modernize. // Declaration of std::auto_ptr.
  20. #if defined(EA_PRAGMA_ONCE_SUPPORTED)
  21. #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
  22. #endif
  23. /// namespace EA
  24. /// The standard Electronic Arts namespace
  25. namespace EA
  26. {
  27. namespace Thread
  28. {
  29. /// class shared_ptr_mt
  30. /// @brief Implements a thread-safe version of shared_ptr.
  31. template<class T>
  32. class shared_ptr_mt
  33. {
  34. private:
  35. /// this_type
  36. /// This is an alias for shared_ptr_mt<T>, this class.
  37. typedef shared_ptr_mt<T> this_type;
  38. /// reference_count_type
  39. /// An internal reference count type. Must be convertable to int
  40. /// so that the public use_count function can work.
  41. typedef EA::Thread::AtomicInt32 reference_count_type;
  42. T* mpValue; /// The owned pointer.
  43. reference_count_type* mpRefCount; /// Reference count for owned pointer.
  44. mutable Futex mMutex; /// Mutex guarding access to this class.
  45. public:
  46. typedef T element_type;
  47. typedef T value_type;
  48. /// shared_ptr_mt
  49. /// Takes ownership of the pointer and sets the reference count
  50. /// to the pointer to 1. It is OK if the input pointer is null.
  51. /// The shared reference count is allocated on the heap via operator new.
  52. /// If an exception occurs during the allocation of the shared
  53. /// reference count, the owned pointer is deleted and the exception
  54. /// is rethrown. A null pointer is given a reference count of 1.
  55. explicit shared_ptr_mt(T* pValue = 0)
  56. : mpValue(pValue), mMutex()
  57. {
  58. // We don't lock our mutex in this function, as this is the constructor
  59. // and we assume that construction is already done in a thread-safe way
  60. // by the owner of this object.
  61. #if defined(EA_COMPILER_NO_EXCEPTIONS) || defined(EA_COMPILER_NO_UNWIND)
  62. mpRefCount = new reference_count_type(1);
  63. #else
  64. EA_DISABLE_VC_WARNING(4571)
  65. try
  66. {
  67. mpRefCount = new reference_count_type(1);
  68. }
  69. catch(...)
  70. {
  71. delete pValue;
  72. //mpRefCount = 0; shouldn't be necessary.
  73. throw;
  74. }
  75. EA_RESTORE_VC_WARNING()
  76. #endif
  77. }
  78. /// shared_ptr_mt
  79. /// Shares ownership of a pointer with another instance of shared_ptr_mt.
  80. /// This function increments the shared reference count on the pointer.
  81. shared_ptr_mt(shared_ptr_mt const& sharedPtr)
  82. : mMutex()
  83. {
  84. // We don't lock our mutex in this function, as this is the constructor
  85. // and we assume that construction is already done in a thread-safe way
  86. // by the owner of this object.
  87. sharedPtr.lock();
  88. mpValue = sharedPtr.mpValue;
  89. mpRefCount = sharedPtr.mpRefCount;
  90. mpRefCount->Increment(); // Atomic operation
  91. sharedPtr.unlock();
  92. }
  93. // Temporarily disabled while we wait for compilers to modernize.
  94. //
  95. // shared_ptr_mt
  96. // Constructs a shared_ptr_mt from a std::auto_ptr. This class
  97. // transfers ownership of the pointer from the auto_ptr by
  98. // calling its release function.
  99. // If an exception occurs during the allocation of the shared
  100. // reference count, the owned pointer is deleted and the exception
  101. // is rethrown.
  102. //explicit shared_ptr_mt(std::auto_ptr<T>& autoPtr)
  103. // : mMutex()
  104. //{
  105. // // We don't lock our mutex in this function, as this is the constructor
  106. // // and we assume that construction is already done in a thread-safe way
  107. // // by the owner of this object.
  108. // mpValue = autoPtr.release();
  109. //
  110. // #if defined(EA_COMPILER_NO_EXCEPTIONS) || defined(EA_COMPILER_NO_UNWIND)
  111. // mpRefCount = new reference_count_type(1);
  112. // #else
  113. // try
  114. // {
  115. // mpRefCount = new reference_count_type(1);
  116. // }
  117. // catch(...)
  118. // {
  119. // delete mpValue;
  120. // mpValue = 0;
  121. // //mpRefCount = 0; shouldn't be necessary.
  122. // throw;
  123. // }
  124. // #endif
  125. //}
  126. /// ~shared_ptr_mt
  127. /// Decrements the reference count for the owned pointer. If the
  128. /// reference count goes to zero, the owned pointer is deleted and
  129. /// the shared reference count is deleted.
  130. ~shared_ptr_mt()
  131. {
  132. lock();
  133. const reference_count_type newRefCount(mpRefCount->Decrement()); // Atomic operation
  134. // EAT_ASSERT(newRefCount >= 0);
  135. if(newRefCount == 0)
  136. {
  137. // we should only be deleting the pointer if it is not null. It is possible that the
  138. // user has created a shared ptr without passing in a value.
  139. if (mpValue)
  140. delete mpValue;
  141. delete mpRefCount;
  142. }
  143. unlock();
  144. }
  145. /// operator=
  146. /// Copies another shared_ptr_mt to this object. Note that this object
  147. /// may already own a shared pointer with another different pointer
  148. /// (but still of the same type) before this call. In that case,
  149. /// this function releases the old pointer, decrementing its reference
  150. /// count and deleting it if zero, takes shared ownership of the new
  151. /// pointer and increments its reference count.
  152. shared_ptr_mt& operator=(shared_ptr_mt const& sharedPtr)
  153. {
  154. // We don't lock mutexes here because we let the swap function
  155. // below do the locking and assignment. The if statement below
  156. // isn't protected within a lock operation because it wouldn't
  157. // help by being so because if mpValue is changing during the
  158. // the execution of this function then the user has an external
  159. // race condition that needs to be managed at that level.
  160. if(mpValue != sharedPtr.mpValue)
  161. {
  162. // The easiest thing to do is to create a temporary and
  163. // copy ourselves ourselves into it. This is a standard
  164. // method for switching pointer ownership in systems like this.
  165. shared_ptr_mt(sharedPtr).swap(*this);
  166. }
  167. return *this;
  168. }
  169. // Temporarily disabled while we wait for compilers to modernize.
  170. //
  171. // operator=
  172. // Transfers ownership of a std::auto_ptr to this class.
  173. //shared_ptr_mt& operator=(std::auto_ptr<T>& autoPtr)
  174. //{
  175. // // We don't lock any mutexes here because we let the swap function do that.
  176. // // EAT_ASSERT(mpValue != autoPtr.get());
  177. // shared_ptr_mt(autoPtr).swap(*this);
  178. // return *this;
  179. //}
  180. // operator=
  181. // We do not defined this function in order to maintain compatibility
  182. // with the currently proposed (2003) C++ standard addition. Use reset instead.
  183. // shared_ptr_mt& operator=(T* pValue);
  184. // {
  185. // reset(pValue);
  186. // return *this;
  187. // }
  188. /// lock
  189. /// @brief Locks our mutex for thread-safe access.
  190. /// It is a const function because const-ness refers to the underlying pointer being
  191. /// held and not this class.
  192. void lock() const
  193. {
  194. mMutex.Lock();
  195. }
  196. /// unlock
  197. /// @brief Unlocks our mutex which was previous locked.
  198. /// It is a const function because const-ness refers to the underlying pointer being
  199. /// held and not this class.
  200. void unlock() const
  201. {
  202. mMutex.Unlock();
  203. }
  204. /// reset
  205. /// Releases the owned pointer and takes ownership of the
  206. /// passed in pointer. If the passed in pointer is the same
  207. /// as the owned pointer, nothing is done. The passed in pointer
  208. /// can be null, in which case the use count is set to 1.
  209. void reset(T* pValue = 0)
  210. {
  211. // We don't lock any mutexes here because we let the swap function do that.
  212. // We don't lock for the 'if' statement below because that wouldn't really buy anything.
  213. if(pValue != mpValue)
  214. {
  215. // The easiest thing to do is to create a temporary and
  216. // copy ourselves ourselves into it. This is a standard
  217. // method for switching pointer ownership in systems like this.
  218. shared_ptr_mt(pValue).swap(*this);
  219. }
  220. }
  221. /// swap
  222. /// Exchanges the owned pointer beween two shared_ptr_mt objects.
  223. void swap(shared_ptr_mt<T>& sharedPtr)
  224. {
  225. lock();
  226. sharedPtr.lock();
  227. // std::swap(mpValue, sharedPtr.mpValue); // Not used so that we can reduce a dependency.
  228. T* const pValue = sharedPtr.mpValue;
  229. sharedPtr.mpValue = mpValue;
  230. mpValue = pValue;
  231. // std::swap(mpRefCount, sharedPtr.mpRefCount); // Not used so that we can reduce a dependency.
  232. reference_count_type* const pRefCount = sharedPtr.mpRefCount;
  233. sharedPtr.mpRefCount = mpRefCount;
  234. mpRefCount = pRefCount;
  235. sharedPtr.unlock();
  236. unlock();
  237. }
  238. /// operator*
  239. /// Returns the owner pointer dereferenced.
  240. /// Example usage:
  241. /// shared_ptr_mt<int> ptr = new int(3);
  242. /// int x = *ptr;
  243. T& operator*() const
  244. {
  245. // We don't lock here because this is essentially a read operation.
  246. // We don't put a SMP read barrier here because we assume the caller does such things.
  247. // EAT_ASSERT(mpValue);
  248. return *mpValue;
  249. }
  250. /// operator->
  251. /// Allows access to the owned pointer via operator->()
  252. /// Example usage:
  253. /// struct X{ void DoSomething(); };
  254. /// shared_ptr_mt<int> ptr = new X;
  255. /// ptr->DoSomething();
  256. T* operator->() const
  257. {
  258. // We don't lock here because this is essentially a read operation.
  259. // We don't put a SMP read barrier here because we assume the caller does such things.
  260. // EAT_ASSERT(mpValue);
  261. return mpValue;
  262. }
  263. /// get
  264. /// Returns the owned pointer. Note that this class does
  265. /// not provide an operator T() function. This is because such
  266. /// a thing (automatic conversion) is deemed unsafe.
  267. /// Example usage:
  268. /// struct X{ void DoSomething(); };
  269. /// shared_ptr_mt<int> ptr = new X;
  270. /// X* pX = ptr.get();
  271. /// pX->DoSomething();
  272. T* get() const
  273. {
  274. // We don't lock here because this is essentially a read operation.
  275. // We don't put a SMP read barrier here because we assume the caller does such things.
  276. return mpValue;
  277. }
  278. /// use_count
  279. /// Returns the reference count on the owned pointer.
  280. /// The return value is one if the owned pointer is null.
  281. int use_count() const
  282. {
  283. // We don't lock here because this is essentially a read operation.
  284. // We don't put a SMP read barrier here because we assume the caller does such things.
  285. // EAT_ASSERT(mpRefCount);
  286. return (int)*mpRefCount;
  287. }
  288. /// unique
  289. /// Returns true if the reference count on the owned pointer is one.
  290. /// The return value is true if the owned pointer is null.
  291. bool unique() const
  292. {
  293. // We don't lock here because this is essentially a read operation.
  294. // We don't put a SMP read barrier here because we assume the caller does such things.
  295. // EAT_ASSERT(mpRefCount);
  296. return (*mpRefCount == 1);
  297. }
  298. /// add_ref
  299. /// Manually increments the reference count on the owned pointer.
  300. /// This is currently disabled because it isn't in part of the
  301. /// proposed C++ language addition.
  302. /// int add_ref()
  303. /// {
  304. /// lock();
  305. /// // EAT_ASSERT(mpRefCount);
  306. /// ++*mpRefCount; // Atomic operation
  307. /// unlock();
  308. /// }
  309. /// release_ref
  310. /// Manually increments the reference count on the owned pointer.
  311. /// If the reference count becomes zero, then the owned pointer
  312. /// is deleted and reset(0) is called. For any given instance of
  313. /// shared_ptr_mt, release_ref can only be called as many times as --
  314. /// but no more than -- the number of times add_ref was called
  315. /// for that same shared_ptr_mt. Otherwise, separate instances of
  316. /// shared_ptr_mt would be left with dangling owned pointer instances.
  317. /// This is currently disabled because it isn't in part of the
  318. /// proposed C++ language addition.
  319. /// int release_ref()
  320. /// {
  321. /// lock();
  322. /// // EAT_ASSERT(mpRefCount);
  323. /// if(*mpRefCount > 1){
  324. /// const int nReturnValue = --*mpRefCount; // Atomic operation
  325. /// unlock();
  326. /// return nReturnValue;
  327. /// }
  328. /// reset(0);
  329. /// unlock();
  330. /// return 0;
  331. /// }
  332. /// Implicit operator bool
  333. /// Allows for using a scoped_ptr as a boolean.
  334. /// Example usage:
  335. /// shared_ptr_mt<int> ptr = new int(3);
  336. /// if(ptr)
  337. /// ++*ptr;
  338. ///
  339. /// Note that below we do not use operator bool(). The reason for this
  340. /// is that booleans automatically convert up to short, int, float, etc.
  341. /// The result is that this: if(scopedPtr == 1) would yield true (bad).
  342. typedef T* (this_type::*bool_)() const;
  343. operator bool_() const
  344. {
  345. // We don't lock here because this is essentially a read operation.
  346. if(mpValue)
  347. return &this_type::get;
  348. return 0;
  349. }
  350. /// operator!
  351. /// This returns the opposite of operator bool; it returns true if
  352. /// the owned pointer is null. Some compilers require this and some don't.
  353. /// shared_ptr_mt<int> ptr = new int(3);
  354. /// if(!ptr)
  355. /// EAT_ASSERT(false);
  356. bool operator!() const
  357. {
  358. // We don't lock here because this is essentially a read operation.
  359. return (mpValue == 0);
  360. }
  361. }; // class shared_ptr_mt
  362. /// get_pointer
  363. /// returns shared_ptr_mt::get() via the input shared_ptr_mt.
  364. template<class T>
  365. inline T* get_pointer(const shared_ptr_mt<T>& sharedPtr)
  366. {
  367. return sharedPtr.get();
  368. }
  369. /// swap
  370. /// Exchanges the owned pointer beween two shared_ptr_mt objects.
  371. /// This non-member version is useful for compatibility of shared_ptr_mt
  372. /// objects with the C++ Standard Library and other libraries.
  373. template<class T>
  374. inline void swap(shared_ptr_mt<T>& sharedPtr1, shared_ptr_mt<T>& sharedPtr2)
  375. {
  376. sharedPtr1.swap(sharedPtr2);
  377. }
  378. /// operator!=
  379. /// Compares two shared_ptr_mt objects for equality. Equality is defined as
  380. /// being true when the pointer shared between two shared_ptr_mt objects is equal.
  381. /// It is debatable what the appropriate definition of equality is between two
  382. /// shared_ptr_mt objects, but we follow the current 2nd generation C++ standard proposal.
  383. template<class T, class U>
  384. inline bool operator==(const shared_ptr_mt<T>& sharedPtr1, const shared_ptr_mt<U>& sharedPtr2)
  385. {
  386. // EAT_ASSERT((sharedPtr1.get() != sharedPtr2.get()) || (sharedPtr1.use_count() == sharedPtr2.use_count()));
  387. return (sharedPtr1.get() == sharedPtr2.get());
  388. }
  389. /// operator!=
  390. /// Compares two shared_ptr_mt objects for inequality. Equality is defined as
  391. /// being true when the pointer shared between two shared_ptr_mt objects is equal.
  392. /// It is debatable what the appropriate definition of equality is between two
  393. /// shared_ptr_mt objects, but we follow the current 2nd generation C++ standard proposal.
  394. template<class T, class U>
  395. inline bool operator!=(const shared_ptr_mt<T>& sharedPtr1, const shared_ptr_mt<U>& sharedPtr2)
  396. {
  397. // EAT_ASSERT((sharedPtr1.get() != sharedPtr2.get()) || (sharedPtr1.use_count() == sharedPtr2.use_count()));
  398. return (sharedPtr1.get() != sharedPtr2.get());
  399. }
  400. /// operator<
  401. /// Returns which shared_ptr_mt is 'less' than the other. Useful when storing
  402. /// sorted containers of shared_ptr_mt objects.
  403. template<class T, class U>
  404. inline bool operator<(const shared_ptr_mt<T>& sharedPtr1, const shared_ptr_mt<U>& sharedPtr2)
  405. {
  406. return (sharedPtr1.get() < sharedPtr2.get()); // Alternatively use: std::less<T*>(a.get(), b.get());
  407. }
  408. } // namespace Thread
  409. } // namespace EA
  410. #endif // EATHREAD_SHARED_PTR_MT_H