Skip to content
Snippets Groups Projects
Commit 431574fd authored by Rye Mutt's avatar Rye Mutt :bread:
Browse files

Replace std::mutex with absl::Mutex in LLInstanceTracker, as its dramatically...

Replace std::mutex with absl::Mutex in LLInstanceTracker, as its dramatically faster at locking and unlocking an uncontended lock.
parent 0d74f9e1
No related branches found
No related tags found
No related merge requests found
...@@ -284,6 +284,7 @@ target_link_libraries( ...@@ -284,6 +284,7 @@ target_link_libraries(
${BOOST_SYSTEM_LIBRARY} ${BOOST_SYSTEM_LIBRARY}
${GOOGLE_PERFTOOLS_LIBRARIES} ${GOOGLE_PERFTOOLS_LIBRARIES}
${URIPARSER_LIBRARIES} ${URIPARSER_LIBRARIES}
absl::synchronization
absl::flat_hash_map absl::flat_hash_map
absl::node_hash_map absl::node_hash_map
absl::strings absl::strings
......
...@@ -34,8 +34,9 @@ ...@@ -34,8 +34,9 @@
#include <typeinfo> #include <typeinfo>
#include <memory> #include <memory>
#include <type_traits> #include <type_traits>
#include <mutex>
#include "mutex.h" #include "absl/synchronization/mutex.h"
#include <boost/iterator/transform_iterator.hpp> #include <boost/iterator/transform_iterator.hpp>
#include <boost/iterator/indirect_iterator.hpp> #include <boost/iterator/indirect_iterator.hpp>
...@@ -52,7 +53,7 @@ namespace LLInstanceTrackerPrivate ...@@ -52,7 +53,7 @@ namespace LLInstanceTrackerPrivate
struct StaticBase struct StaticBase
{ {
// We need to be able to lock static data while manipulating it. // 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&); void logerrs(const char* cls, const std::string&, const std::string&, const std::string&);
...@@ -80,7 +81,7 @@ class LLInstanceTracker ...@@ -80,7 +81,7 @@ class LLInstanceTracker
{ {
InstanceMap mMap; InstanceMap mMap;
}; };
typedef llthread::LockStatic<StaticData> LockStatic; typedef llthread::LockStaticAbsl<StaticData> LockStatic;
public: public:
// snapshot of std::pair<const KEY, std::shared_ptr<T>> pairs // snapshot of std::pair<const KEY, std::shared_ptr<T>> pairs
...@@ -307,7 +308,7 @@ class LLInstanceTracker<T, void, KEY_COLLISION_BEHAVIOR> ...@@ -307,7 +308,7 @@ class LLInstanceTracker<T, void, KEY_COLLISION_BEHAVIOR>
{ {
InstanceSet mSet; InstanceSet mSet;
}; };
typedef llthread::LockStatic<StaticData> LockStatic; typedef llthread::LockStaticAbsl<StaticData> LockStatic;
public: public:
/** /**
......
...@@ -13,7 +13,9 @@ ...@@ -13,7 +13,9 @@
#if ! defined(LL_LOCKSTATIC_H) #if ! defined(LL_LOCKSTATIC_H)
#define LL_LOCKSTATIC_H #define LL_LOCKSTATIC_H
#include "mutex.h" // std::unique_lock #include <mutex>
#include "absl/synchronization/mutex.h"
namespace llthread namespace llthread
{ {
...@@ -68,6 +70,100 @@ class LockStatic ...@@ -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 } // llthread namespace
#endif /* ! defined(LL_LOCKSTATIC_H) */ #endif /* ! defined(LL_LOCKSTATIC_H) */
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment