diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 8762d54f9c27512163e1429d629ac0871111bf43..afe1f9abb9b42447b18e74c55b093acd924991f1 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -284,6 +284,7 @@ target_link_libraries( ${BOOST_SYSTEM_LIBRARY} ${GOOGLE_PERFTOOLS_LIBRARIES} ${URIPARSER_LIBRARIES} + absl::synchronization absl::flat_hash_map absl::node_hash_map absl::strings diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h index 92e0a13683e10b64d89a69d6f429ff20daa838a2..eadb5ec17ae5eff9f910bfd1fbafbdaec2268b98 100644 --- a/indra/llcommon/llinstancetracker.h +++ b/indra/llcommon/llinstancetracker.h @@ -34,8 +34,9 @@ #include <typeinfo> #include <memory> #include <type_traits> +#include <mutex> -#include "mutex.h" +#include "absl/synchronization/mutex.h" #include <boost/iterator/transform_iterator.hpp> #include <boost/iterator/indirect_iterator.hpp> @@ -52,7 +53,7 @@ namespace LLInstanceTrackerPrivate struct StaticBase { // We need to be able to lock static data while manipulating it. - std::mutex mMutex; + absl::Mutex mMutex; }; void logerrs(const char* cls, const std::string&, const std::string&, const std::string&); @@ -80,7 +81,7 @@ class LLInstanceTracker { InstanceMap mMap; }; - typedef llthread::LockStatic<StaticData> LockStatic; + typedef llthread::LockStaticAbsl<StaticData> LockStatic; public: // snapshot of std::pair<const KEY, std::shared_ptr<T>> pairs @@ -307,7 +308,7 @@ class LLInstanceTracker<T, void, KEY_COLLISION_BEHAVIOR> { InstanceSet mSet; }; - typedef llthread::LockStatic<StaticData> LockStatic; + typedef llthread::LockStaticAbsl<StaticData> LockStatic; public: /** diff --git a/indra/llcommon/lockstatic.h b/indra/llcommon/lockstatic.h index 96c53c64732b6ab45900b1b53e1abab1e43becff..69c8026a5e101c3df15eaaf402988753660c6020 100644 --- a/indra/llcommon/lockstatic.h +++ b/indra/llcommon/lockstatic.h @@ -13,7 +13,9 @@ #if ! defined(LL_LOCKSTATIC_H) #define LL_LOCKSTATIC_H -#include "mutex.h" // std::unique_lock +#include <mutex> + +#include "absl/synchronization/mutex.h" namespace llthread { @@ -68,6 +70,100 @@ class LockStatic } }; + +// The same as above but for absl::Mutex +template <typename Static> +class LockStaticAbsl +{ + typedef absl::ReleasableMutexLock lock_t; +public: + LockStaticAbsl() : + mData(getStatic()), + mLock(&mData->mMutex) + {} + Static* get() const { return mData; } + operator Static* () const { return get(); } + Static* operator->() const { return get(); } + // sometimes we must explicitly unlock... + void unlock() + { + // but once we do, access is no longer permitted + mData = nullptr; + mLock.Release(); + } +protected: + Static* mData; + lock_t mLock; +private: + Static* getStatic() + { + // Static::mMutex must be function-local static rather than class- + // static. Some of our consumers must function properly (therefore + // lock properly) even when the containing module's static variables + // have not yet been runtime-initialized. A mutex requires + // construction. A static class member might not yet have been + // constructed. + // + // We could store a dumb mutex_t*, notice when it's NULL and allocate a + // heap mutex -- but that's vulnerable to race conditions. And we can't + // defend the dumb pointer with another mutex. + // + // We could store a std::atomic<mutex_t*> -- but a default-constructed + // std::atomic<T> does not contain a valid T, even a default-constructed + // T! Which means std::atomic, too, requires runtime initialization. + // + // But a function-local static is guaranteed to be initialized exactly + // once: the first time control reaches that declaration. + static Static sData; + return &sData; + } +}; + +// Instantiate this template to obtain a pointer to the canonical static +// instance of Static with no lock. Aka NoOp +template <typename Static> +class LockStaticNoOp +{ +public: + LockStaticNoOp(): + mData(getStatic()) + {} + Static* get() const { return mData; } + operator Static*() const { return get(); } + Static* operator->() const { return get(); } + // sometimes we must explicitly unlock... + void unlock() + { + // but once we do, access is no longer permitted + mData = nullptr; + } +protected: + Static* mData; +private: + Static* getStatic() + { + // Static::mMutex must be function-local static rather than class- + // static. Some of our consumers must function properly (therefore + // lock properly) even when the containing module's static variables + // have not yet been runtime-initialized. A mutex requires + // construction. A static class member might not yet have been + // constructed. + // + // We could store a dumb mutex_t*, notice when it's NULL and allocate a + // heap mutex -- but that's vulnerable to race conditions. And we can't + // defend the dumb pointer with another mutex. + // + // We could store a std::atomic<mutex_t*> -- but a default-constructed + // std::atomic<T> does not contain a valid T, even a default-constructed + // T! Which means std::atomic, too, requires runtime initialization. + // + // But a function-local static is guaranteed to be initialized exactly + // once: the first time control reaches that declaration. + static Static sData; + return &sData; + } +}; + } // llthread namespace #endif /* ! defined(LL_LOCKSTATIC_H) */