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

Replace std::recursive_mutex with LLMutex inside LLSingleton as the recursive...

Replace std::recursive_mutex with LLMutex inside LLSingleton as the recursive mutex impl to avoid virtual dispatch overhead of std::mutex on msvc
parent 431574fd
No related branches found
No related tags found
No related merge requests found
...@@ -31,9 +31,12 @@ ...@@ -31,9 +31,12 @@
#include "llthread.h" #include "llthread.h"
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include "mutex.h" #include "absl/synchronization/mutex.h"
#include <mutex>
#include <condition_variable> #include <condition_variable>
//============================================================================ //============================================================================
#define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO) #define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO)
...@@ -42,6 +45,8 @@ ...@@ -42,6 +45,8 @@
#include <map> #include <map>
#endif #endif
// This is a recursive mutex.
class LL_COMMON_API LLMutex class LL_COMMON_API LLMutex
{ {
public: public:
...@@ -91,6 +96,10 @@ class LLMutexLock ...@@ -91,6 +96,10 @@ class LLMutexLock
mMutex->lock(); mMutex->lock();
} }
~LLMutexLock() ~LLMutexLock()
{
unlock();
}
void unlock()
{ {
if (mMutex) if (mMutex)
mMutex->unlock(); mMutex->unlock();
...@@ -126,6 +135,59 @@ class LLMutexTrylock ...@@ -126,6 +135,59 @@ class LLMutexTrylock
bool mLocked; bool mLocked;
}; };
// This is here due to include order issues wrt llmutex.h and lockstatic.h
namespace llthread
{
template <typename Static>
class LockStaticLL
{
typedef LLMutexLock lock_t;
public:
LockStaticLL() :
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.unlock();
}
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;
}
};
}
/** /**
* @class LLScopedLock * @class LLScopedLock
* @brief Small class to help lock and unlock mutexes. * @brief Small class to help lock and unlock mutexes.
...@@ -164,4 +226,48 @@ class LL_COMMON_API LLScopedLock : private boost::noncopyable ...@@ -164,4 +226,48 @@ class LL_COMMON_API LLScopedLock : private boost::noncopyable
std::mutex* mMutex; std::mutex* mMutex;
}; };
class AbslMutexMaybeTrylock
{
public:
AbslMutexMaybeTrylock(absl::Mutex* mutex)
: mMutex(mutex),
mLocked(false)
{
if (mMutex)
mLocked = mMutex->TryLock();
}
AbslMutexMaybeTrylock(absl::Mutex* mutex, U32 aTries, U32 delay_ms = 10)
: mMutex(mutex),
mLocked(false)
{
if (!mMutex)
return;
for (U32 i = 0; i < aTries; ++i)
{
mLocked = mMutex->TryLock();
if (mLocked)
break;
std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
}
}
~AbslMutexMaybeTrylock()
{
if (mMutex && mLocked)
mMutex->Unlock();
}
bool isLocked() const
{
return mLocked;
}
private:
absl::Mutex* mMutex;
bool mLocked;
};
#endif // LL_LLMUTEX_H #endif // LL_LLMUTEX_H
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include "lldependencies.h" #include "lldependencies.h"
#include "llexception.h" #include "llexception.h"
#include "llcoros.h" #include "llcoros.h"
#include "llmutex.h"
#include <algorithm> #include <algorithm>
#include <iostream> // std::cerr in dire emergency #include <iostream> // std::cerr in dire emergency
#include <sstream> #include <sstream>
...@@ -63,8 +64,8 @@ class LLSingletonBase::MasterList final : ...@@ -63,8 +64,8 @@ class LLSingletonBase::MasterList final :
// manipulating some data in the master list, we must also check whether // manipulating some data in the master list, we must also check whether
// it's safe to log -- which involves querying a different LLSingleton -- // it's safe to log -- which involves querying a different LLSingleton --
// which requires accessing the master list. // which requires accessing the master list.
typedef std::recursive_mutex mutex_t; typedef LLMutex mutex_t;
typedef std::unique_lock<mutex_t> lock_t; typedef LLMutexLock lock_t;
mutex_t mMutex; mutex_t mMutex;
...@@ -76,7 +77,7 @@ class LLSingletonBase::MasterList final : ...@@ -76,7 +77,7 @@ class LLSingletonBase::MasterList final :
public: public:
Lock(): Lock():
mMasterList(MasterList::instance()), mMasterList(MasterList::instance()),
mLock(mMasterList.mMutex) mLock(&mMasterList.mMutex)
{} {}
Lock(const Lock&) = delete; Lock(const Lock&) = delete;
Lock& operator=(const Lock&) = delete; Lock& operator=(const Lock&) = delete;
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "mutex.h" #include "mutex.h"
#include "lockstatic.h" #include "lockstatic.h"
#include "llthread.h" // on_main_thread() #include "llthread.h" // on_main_thread()
#include "llmutex.h"
#include "llmainthreadtask.h" #include "llmainthreadtask.h"
class LLSingletonBase: private boost::noncopyable class LLSingletonBase: private boost::noncopyable
...@@ -292,13 +293,13 @@ class LLSingleton : public LLSingletonBase ...@@ -292,13 +293,13 @@ class LLSingleton : public LLSingletonBase
{ {
// Use a recursive_mutex in case of constructor circularity. With a // Use a recursive_mutex in case of constructor circularity. With a
// non-recursive mutex, that would result in deadlock. // non-recursive mutex, that would result in deadlock.
typedef std::recursive_mutex mutex_t; typedef LLMutex mutex_t;
mutex_t mMutex; // LockStatic looks for mMutex mutex_t mMutex; // LockStatic looks for mMutex
EInitState mInitState{UNINITIALIZED}; EInitState mInitState{UNINITIALIZED};
DERIVED_TYPE* mInstance{nullptr}; DERIVED_TYPE* mInstance{nullptr};
}; };
typedef llthread::LockStatic<SingletonData> LockStatic; typedef llthread::LockStaticLL<SingletonData> LockStatic;
// Allow LLParamSingleton subclass -- but NOT DERIVED_TYPE itself -- to // Allow LLParamSingleton subclass -- but NOT DERIVED_TYPE itself -- to
// access our private members. // access our private members.
......
...@@ -24,11 +24,11 @@ namespace llthread ...@@ -24,11 +24,11 @@ namespace llthread
// instance of Static while holding a lock on that instance. Use of // instance of Static while holding a lock on that instance. Use of
// Static::mMutex presumes that Static declares some suitable mMutex. // Static::mMutex presumes that Static declares some suitable mMutex.
template <typename Static> template <typename Static>
class LockStatic class LockStaticStd
{ {
typedef std::unique_lock<decltype(Static::mMutex)> lock_t; typedef std::unique_lock<decltype(Static::mMutex)> lock_t;
public: public:
LockStatic(): LockStaticStd():
mData(getStatic()), mData(getStatic()),
mLock(mData->mMutex) mLock(mData->mMutex)
{} {}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment