diff --git a/indra/llcommon/aithreadsafe.h b/indra/llcommon/aithreadsafe.h index 0cff3b77a..e22f587e2 100644 --- a/indra/llcommon/aithreadsafe.h +++ b/indra/llcommon/aithreadsafe.h @@ -49,7 +49,10 @@ template struct AIReadAccessConst; template struct AIReadAccess; template struct AIWriteAccess; +template struct AIAccessConst; template struct AIAccess; +template struct AISTAccessConst; +template struct AISTAccess; #if LL_WINDOWS template class AIThreadSafeBits; @@ -398,6 +401,7 @@ class AIThreadSafeSimple : public AIThreadSafeBits { protected: // Only this one may access the object (through ptr()). + friend struct AIAccessConst; friend struct AIAccess; // Locking control. @@ -509,13 +513,13 @@ public: }; /** - * @brief Write lock object and provide read/write access. + * @brief Write lock object and provide read access. */ template -struct AIAccess +struct AIAccessConst { - //! Construct a AIAccess from a non-constant AIThreadSafeSimple. - AIAccess(AIThreadSafeSimple& wrapper) : mWrapper(wrapper) + //! Construct a AIAccessConst from a constant AIThreadSafeSimple. + AIAccessConst(AIThreadSafeSimple const& wrapper) : mWrapper(const_cast&>(wrapper)) #if AI_NEED_ACCESS_CC , mIsCopyConstructed(false) #endif @@ -524,12 +528,12 @@ struct AIAccess } //! Access the underlaying object for (read and) write access. - T* operator->() const { return this->mWrapper.ptr(); } + T const* operator->() const { return this->mWrapper.ptr(); } //! Access the underlaying object for (read and) write access. - T& operator*() const { return *this->mWrapper.ptr(); } + T const& operator*() const { return *this->mWrapper.ptr(); } - ~AIAccess() + ~AIAccessConst() { #if AI_NEED_ACCESS_CC if (mIsCopyConstructed) return; @@ -538,17 +542,204 @@ struct AIAccess } protected: - AIThreadSafeSimple& mWrapper; //!< Reference to the object that we provide access to. + AIThreadSafeSimple& mWrapper; //!< Reference to the object that we provide access to. #if AI_NEED_ACCESS_CC bool mIsCopyConstructed; public: - AIAccess(AIAccess const& orig) : mWrapper(orig.mWrapper), mIsCopyConstructed(true) { } + AIAccessConst(AIAccessConst const& orig) : mWrapper(orig.mWrapper), mIsCopyConstructed(true) { } #else private: // Disallow copy constructing directly. - AIAccess(AIAccess const&); + AIAccessConst(AIAccessConst const&); #endif }; +/** + * @brief Write lock object and provide read/write access. + */ +template +struct AIAccess : public AIAccessConst +{ + //! Construct a AIAccess from a non-constant AIThreadSafeSimple. + AIAccess(AIThreadSafeSimple& wrapper) : AIAccessConst(wrapper) { } + + //! Access the underlaying object for (read and) write access. + T* operator->() const { return this->mWrapper.ptr(); } + + //! Access the underlaying object for (read and) write access. + T& operator*() const { return *this->mWrapper.ptr(); } +}; + +/** + * @brief A wrapper class for objects that should only be accessed by a single thread. + * + * Use AITHREADSAFESINGLETHREAD to define instances of any type, and use AISTAccess + * to get access to the instance. + * + * For example, + * + * + * class Foo { public: Foo(int, int); }; + * + * AITHREADSAFESINGLETHREAD(Foo, foo, (2, 3)); + * + * AISTAccess foo_w(foo); + * // Use foo_w-> for read and write access. + */ +template +class AIThreadSafeSingleThread : public AIThreadSafeBits +{ +protected: + // Only these one may access the object (through ptr()). + friend struct AISTAccessConst; + friend struct AISTAccess; + + // For use by AIThreadSafeSingleThreadDC. + AIThreadSafeSingleThread(void) +#ifdef LL_DEBUG + : mAccessed(false) +#endif + { } + +#ifdef LL_DEBUG + mutable bool mAccessed; + mutable apr_os_thread_t mTheadID; + + void accessed(void) const + { + if (!mAccessed) + { + mAccessed = true; + mTheadID = apr_os_thread_current(); + } + else + { + llassert_always(apr_os_thread_equal(mTheadID, apr_os_thread_current())); + } + } +#endif + +public: + // Only for use by AITHREADSAFESINGLETHREAD, see below. + AIThreadSafeSingleThread(T* object) +#ifdef LL_DEBUG + : mAccessed(false) +#endif + { + llassert(object == AIThreadSafeBits::ptr()); + } +}; + +/** + * @brief A wrapper class for objects that should only be accessed by a single thread. + * + * This class is the same as an AIThreadSafeSingleThread wrapper, except that it can only + * be used for default constructed objects. + * + * For example, instead of + * + * + * Foo foo; + * + * + * One would use + * + * + * AIThreadSafeSingleThreadDC foo; + * + * + * The advantage over AITHREADSAFESINGLETHREAD is that this object can be allocated with + * new on the heap. For example: + * + * + * AIThreadSafeSingleThreadDC* ptr = new AIThreadSafeSingleThreadDC; + * + * + * which is not possible with AITHREADSAFESINGLETHREAD. + */ +template +class AIThreadSafeSingleThreadDC : public AIThreadSafeSingleThread +{ +public: + // Construct a wrapper around a default constructed object. + AIThreadSafeSingleThreadDC(void) { new (AIThreadSafeSingleThread::ptr()) T; } +}; + +/** + * @brief Instantiate a static, global or local object of a given type wrapped in AIThreadSafeSingleThread, using an arbitrary constructor. + * + * For example, instead of doing + * + * + * Foo foo(x, y); + * static Bar bar; + * + * + * One can instantiate a thread-safe instance with + * + * + * AITHREADSAFESINGLETHREAD(Foo, foo, (x, y)); + * static AITHREADSAFESINGLETHREAD(Bar, bar, ); + * + * + * Note: This macro does not allow to allocate such object on the heap. + * If that is needed, have a look at AIThreadSafeSingleThreadDC. + */ +#define AITHREADSAFESINGLETHREAD(type, var, paramlist) AIThreadSafeSingleThread var(new (var.memory()) type paramlist) + +/** + * @brief Access single threaded object for read access. + */ +template +struct AISTAccessConst +{ + //! Construct a AISTAccessConst from a constant AIThreadSafeSingleThread. + AISTAccessConst(AIThreadSafeSingleThread const& wrapper) : mWrapper(const_cast&>(wrapper)) + { +#if LL_DEBUG + wrapper.accessed(); +#endif + } + + //! Access the underlaying object for read access. + T const* operator->() const { return this->mWrapper.ptr(); } + + //! Access the underlaying object for read write access. + T const& operator*() const { return *this->mWrapper.ptr(); } + +protected: + AIThreadSafeSingleThread& mWrapper; //!< Reference to the object that we provide access to. + +#if AI_NEED_ACCESS_CC +public: + AISTAccessConst(AISTAccessConst const& orig) : mWrapper(orig.mWrapper) { } +#else +private: + // Disallow copy constructing directly. + AISTAccessConst(AISTAccessConst const&); +#endif +}; + +/** + * @brief Access single threaded object for read/write access. + */ +template +struct AISTAccess : public AISTAccessConst +{ + //! Construct a AISTAccess from a non-constant AIThreadSafeSingleThread. + AISTAccess(AIThreadSafeSingleThread& wrapper) : AISTAccessConst(wrapper) + { +#if LL_DEBUG + wrapper.accessed(); +#endif + } + + //! Access the underlaying object for (read and) write access. + T* operator->() const { return this->mWrapper.ptr(); } + + //! Access the underlaying object for (read and) write access. + T& operator*() const { return *this->mWrapper.ptr(); } +}; + #endif