shared_array_mt.h 14 KB

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