| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- ///////////////////////////////////////////////////////////////////////////////
- // Copyright (c) Electronic Arts Inc. All rights reserved.
- ///////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////
- // This is a multithread-safe version of shared_array_mt.
- // For basic documentation, see shared_array_mt.
- ///////////////////////////////////////////////////////////////////////////////
- #ifndef EATHREAD_SHARED_ARRAY_MT_H
- #define EATHREAD_SHARED_ARRAY_MT_H
- #ifndef INCLUDED_eabase_H
- #include <EABase/eabase.h>
- #endif
- #ifndef EATHREAD_EATHREAD_FUTEX_H
- #include <eathread/eathread_futex.h>
- #endif
- #include <stddef.h> // More properly: #include <cstddef> // Definition of std::ptrdiff_t
- #if defined(EA_PRAGMA_ONCE_SUPPORTED)
- #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.
- #endif
- /// namespace EA
- /// The standard Electronic Arts namespace
- namespace EA
- {
- namespace Thread
- {
- /// class shared_array_mt
- /// A shared_array_mt is the same as shared_ptr but for arrays.
- template<class T>
- class shared_array_mt
- {
- private:
- /// this_type
- /// This is an alias for shared_array_mt<T>, this class.
- typedef shared_array_mt<T> this_type;
- /// reference_count_type
- /// An internal reference count type. Must be convertable to int
- /// so that the public use_count function can work.
- typedef EA::Thread::AtomicInt32 reference_count_type;
- T* mpArray; /// The owned pointer. Points to an array of T.
- reference_count_type* mpRefCount; /// Reference count for owned pointer.
- mutable Futex mMutex; /// Mutex guarding access to this class.
- public:
- typedef T element_type;
- typedef T value_type;
- /// shared_array_mt
- /// Takes ownership of the pointer and sets the reference count
- /// to the pointer to 1. It is OK if the input pointer is null.
- /// The shared reference count is allocated on the heap via operator new.
- /// If an exception occurs during the allocation of the shared
- /// reference count, the owned pointer is deleted and the exception
- /// is rethrown. A null pointer is given a reference count of 1.
- explicit shared_array_mt(T* pArray = 0)
- : mpArray(pArray), mMutex()
- {
- // We don't lock our mutex in this function, as this is the constructor
- // and we assume that construction is already done in a thread-safe way
- // by the owner of this object.
- #if defined(EA_COMPILER_NO_EXCEPTIONS) || defined(EA_COMPILER_NO_UNWIND)
- mpRefCount = new reference_count_type(1);
- #else
- EA_DISABLE_VC_WARNING(4571)
- try
- {
- mpRefCount = new reference_count_type(1);
- }
- catch(...)
- {
- delete[] mpArray;
- //mpRefCount = 0; shouldn't be necessary.
- throw;
- }
- EA_RESTORE_VC_WARNING()
- #endif
- }
- /// shared_array_mt
- /// Shares ownership of a pointer with another instance of shared_array_mt.
- /// This function increments the shared reference count on the pointer.
- shared_array_mt(shared_array_mt const& sharedArray)
- : mMutex()
- {
- sharedArray.lock();
- mpArray = sharedArray.mpArray;
- mpRefCount = sharedArray.mpRefCount;
- mpRefCount->Increment(); // Atomic operation
- sharedArray.unlock();
- }
- /// ~shared_array_mt
- /// Decrements the reference count for the owned pointer. If the
- /// reference count goes to zero, the owned pointer is deleted and
- /// the shared reference count is deleted.
- ~shared_array_mt()
- {
- lock();
- const reference_count_type newRefCount(mpRefCount->Decrement()); // Atomic operation
- // EAT_ASSERT(newRefCount >= 0);
- if(newRefCount == 0)
- {
- delete[] mpArray;
- delete mpRefCount;
- }
- unlock();
- }
- /// operator=
- /// Copies another shared_array_mt to this object. Note that this object
- /// may already own a shared pointer with another different pointer
- /// (but still of the same type) before this call. In that case,
- /// this function releases the old pointer, decrementing its reference
- /// count and deleting it if zero, takes shared ownership of the new
- /// pointer and increments its reference count.
- shared_array_mt& operator=(shared_array_mt const& sharedArray)
- {
- // We don't lock mutexes here because we let the swap function
- // below do the locking and assignment. The if statement below
- // isn't protected within a lock operation because it wouldn't
- // help by being so because if mpValue is changing during the
- // the execution of this function then the user has an external
- // race condition that needs to be managed at that level.
- if(mpArray != sharedArray.mpArray)
- {
- // The easiest thing to do is to create a temporary and
- // copy ourselves ourselves into it. This is a standard
- // method for switching pointer ownership in systems like this.
- shared_array_mt(sharedArray).swap(*this);
- }
- return *this;
- }
- // operator=
- // We do not defined this function in order to maintain compatibility
- // with the currently proposed (2003) C++ standard addition. Use reset instead.
- // shared_array_mt& operator=(T* pValue)
- // {
- // reset(pValue);
- // return *this;
- // }
- /// lock
- /// @brief Locks our mutex for thread-safe access.
- /// It is a const function because const-ness refers to the underlying pointer being
- /// held and not this class.
- void lock() const
- {
- mMutex.Lock();
- }
- /// unlock
- /// @brief Unlocks our mutex which was previous locked.
- /// It is a const function because const-ness refers to the underlying pointer being
- /// held and not this class.
- void unlock() const
- {
- mMutex.Unlock();
- }
- /// reset
- /// Releases the owned pointer and takes ownership of the
- /// passed in pointer. If the passed in pointer is the same
- /// as the owned pointer, nothing is done. The passed in pointer
- /// can be null, in which case the use count is set to 1.
- void reset(T* pArray = 0)
- {
- // We don't lock any mutexes here because we let the swap function do that.
- // We don't lock for the 'if' statement below because that wouldn't really buy anything.
- if(pArray != mpArray)
- {
- // The easiest thing to do is to create a temporary and
- // copy ourselves ourselves into it. This is a standard
- // method for switching pointer ownership in systems like this.
- shared_array_mt(pArray).swap(*this);
- }
- }
- /// swap
- /// Exchanges the owned pointer beween two shared_array_mt objects.
- void swap(shared_array_mt<T>& sharedArray)
- {
- lock();
- sharedArray.lock();
- // std::swap(mpArray, sharedArray.mpArray); // Not used so that we can reduce a dependency.
- T* const pArray = sharedArray.mpArray;
- sharedArray.mpArray = mpArray;
- mpArray = pArray;
- // std::swap(mpRefCount, sharedArray.mpRefCount); // Not used so that we can reduce a dependency.
- reference_count_type* const pRefCount = sharedArray.mpRefCount;
- sharedArray.mpRefCount = mpRefCount;
- mpRefCount = pRefCount;
- sharedArray.unlock();
- unlock();
- }
- /// operator[]
- /// Returns a reference to the specified item in the owned pointer
- /// array.
- /// Example usage:
- /// shared_array_mt<int> ptr = new int[6];
- /// int x = ptr[2];
- T& operator[](ptrdiff_t i) const
- {
- // We don't lock here because this is essentially a read operation.
- // We don't put a SMP read barrier here because we assume the caller does such things.
- // EAT_ASSERT(mpArray && (i >= 0));
- return mpArray[i];
- }
- /// operator*
- /// Returns the owner pointer dereferenced.
- /// Example usage:
- /// shared_array_mt<int> ptr = new int(3);
- /// int x = *ptr;
- T& operator*() const
- {
- // We don't lock here because this is essentially a read operation.
- // We don't put a SMP read barrier here because we assume the caller does such things.
- // EAT_ASSERT(mpArray);
- return *mpArray;
- }
- /// operator->
- /// Allows access to the owned pointer via operator->()
- /// Example usage:
- /// struct X{ void DoSomething(); };
- /// shared_array_mt<int> ptr = new X;
- /// ptr->DoSomething();
- T* operator->() const
- {
- // We don't lock here because this is essentially a read operation.
- // We don't put a SMP read barrier here because we assume the caller does such things.
- // EAT_ASSERT(mpArray);
- return mpArray;
- }
- /// get
- /// Returns the owned pointer. Note that this class does
- /// not provide an operator T() function. This is because such
- /// a thing (automatic conversion) is deemed unsafe.
- /// Example usage:
- /// struct X{ void DoSomething(); };
- /// shared_array_mt<int> ptr = new X;
- /// X* pX = ptr.get();
- /// pX->DoSomething();
- T* get() const
- {
- // We don't lock here because this is essentially a read operation.
- // We don't put a SMP read barrier here because we assume the caller does such things.
- return mpArray;
- }
- /// use_count
- /// Returns the reference count on the owned pointer.
- /// The return value is one if the owned pointer is null.
- int use_count() const
- {
- // We don't lock here because this is essentially a read operation.
- // We don't put a SMP read barrier here because we assume the caller does such things.
- // EAT_ASSERT(mpRefCount);
- return (int)*mpRefCount;
- }
- /// unique
- /// Returns true if the reference count on the owned pointer is one.
- /// The return value is true if the owned pointer is null.
- bool unique() const
- {
- // We don't lock here because this is essentially a read operation.
- // We don't put a SMP read barrier here because we assume the caller does such things.
- // EAT_ASSERT(mpRefCount);
- return (*mpRefCount == 1);
- }
- /// add_ref
- /// Manually increments the reference count on the owned pointer.
- /// This is currently disabled because it isn't in part of the
- /// proposed C++ language addition.
- /// int add_ref()
- /// {
- /// lock();
- /// // EAT_ASSERT(mpRefCount);
- /// ++*mpRefCount; // Atomic operation
- /// unlock();
- /// }
- /// release_ref
- /// Manually increments the reference count on the owned pointer.
- /// If the reference count becomes zero, then the owned pointer
- /// is deleted and reset(0) is called. For any given instance of
- /// shared_ptr, release_ref can only be called as many times as --
- /// but no more than -- the number of times add_ref was called
- /// for that same shared_ptr. Otherwise, separate instances of
- /// shared_ptr would be left with dangling owned pointer instances.
- /// This is currently disabled because it isn't in part of the
- /// proposed C++ language addition.
- /// int release_ref()
- /// {
- /// lock();
- /// // EAT_ASSERT(mpRefCount);
- /// if(*mpRefCount > 1){
- /// const int nReturnValue = --*mpRefCount; // Atomic operation
- /// unlock();
- /// return nReturnValue;
- /// }
- /// reset(0);
- /// unlock();
- /// return 0;
- /// }
- /// Implicit operator bool
- /// Allows for using a scoped_ptr as a boolean.
- /// Example usage:
- /// shared_array_mt<int> ptr = new int(3);
- /// if(ptr)
- /// ++*ptr;
- ///
- /// Note that below we do not use operator bool(). The reason for this
- /// is that booleans automatically convert up to short, int, float, etc.
- /// The result is that this: if(scopedPtr == 1) would yield true (bad).
- typedef T* (this_type::*bool_)() const;
- operator bool_() const
- {
- // We don't lock here because this is essentially a read operation.
- if(mpArray)
- return &this_type::get;
- return 0;
- }
- /// operator!
- /// This returns the opposite of operator bool; it returns true if
- /// the owned pointer is null. Some compilers require this and some don't.
- /// shared_array_mt<int> ptr = new int(3);
- /// if(!ptr)
- /// EAT_ASSERT(false);
- bool operator!() const
- {
- // We don't lock here because this is essentially a read operation.
- return (mpArray == 0);
- }
- }; // class shared_array_mt
- /// get_pointer
- /// returns shared_array_mt::get() via the input shared_array_mt.
- template<class T>
- inline T* get_pointer(const shared_array_mt<T>& sharedArray)
- {
- return sharedArray.get();
- }
- /// swap
- /// Exchanges the owned pointer beween two shared_array_mt objects.
- /// This non-member version is useful for compatibility of shared_array_mt
- /// objects with the C++ Standard Library and other libraries.
- template<class T>
- inline void swap(shared_array_mt<T>& sharedArray1, shared_array_mt<T>& sharedArray2)
- {
- sharedArray1.swap(sharedArray2);
- }
- /// operator!=
- /// Compares two shared_array_mt objects for equality. Equality is defined as
- /// being true when the pointer shared between two shared_array_mt objects is equal.
- /// It is debatable what the appropriate definition of equality is between two
- /// shared_array_mt objects, but we follow the current 2nd generation C++ standard proposal.
- template<class T, class U>
- inline bool operator==(const shared_array_mt<T>& sharedArray1, const shared_array_mt<U>& sharedArray2)
- {
- // EAT_ASSERT((sharedArray1.get() != sharedArray2.get()) || (sharedArray1.use_count() == sharedArray2.use_count()));
- return (sharedArray1.get() == sharedArray2.get());
- }
- /// operator!=
- /// Compares two shared_array_mt objects for inequality. Equality is defined as
- /// being true when the pointer shared between two shared_array_mt objects is equal.
- /// It is debatable what the appropriate definition of equality is between two
- /// shared_array_mt objects, but we follow the current 2nd generation C++ standard proposal.
- template<class T, class U>
- inline bool operator!=(const shared_array_mt<T>& sharedArray1, const shared_array_mt<U>& sharedArray2)
- {
- // EAT_ASSERT((sharedArray1.get() != sharedArray2.get()) || (sharedArray1.use_count() == sharedArray2.use_count()));
- return (sharedArray1.get() != sharedArray2.get());
- }
- /// operator<
- /// Returns which shared_array_mt is 'less' than the other. Useful when storing
- /// sorted containers of scoped_ptr objects.
- template<class T, class U>
- inline bool operator<(const shared_array_mt<T>& sharedArray1, const shared_array_mt<U>& sharedArray2)
- {
- return (sharedArray1.get() < sharedArray2.get()); // Alternatively use: std::less<T*>(a.get(), b.get());
- }
- } // namespace Thread
- } // namespace EA
- #endif // EATHREAD_SHARED_ARRAY_MT_H
|