diff --git a/src/sync.cpp b/src/sync.cpp index 7849ba9d84..4ed6bf6c89 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -223,8 +223,10 @@ void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexTyp } template void EnterCritical(const char*, const char*, int, Mutex*, bool); template void EnterCritical(const char*, const char*, int, RecursiveMutex*, bool); +template void EnterCritical(const char*, const char*, int, SharedMutex*, bool); template void EnterCritical(const char*, const char*, int, std::mutex*, bool); template void EnterCritical(const char*, const char*, int, std::recursive_mutex*, bool); +template void EnterCritical(const char*, const char*, int, std::shared_mutex*, bool); void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) { @@ -291,6 +293,7 @@ void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, } template void AssertLockHeldInternal(const char*, const char*, int, Mutex*); template void AssertLockHeldInternal(const char*, const char*, int, RecursiveMutex*); +template void AssertLockHeldInternal(const char*, const char*, int, SharedMutex*); void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) { diff --git a/src/sync.h b/src/sync.h index 111bf61f01..8db0b80e25 100644 --- a/src/sync.h +++ b/src/sync.h @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -115,6 +116,21 @@ public: #endif // __clang__ }; +template +class LOCKABLE SharedAnnotatedMixin : public AnnotatedMixin +{ +public: + bool try_shared_lock() SHARED_TRYLOCK_FUNCTION(true) + { + return PARENT::try_shared_lock(); + } + void shared_lock() SHARED_LOCK_FUNCTION() + { + PARENT::shared_lock(); + } + using SharedLock = std::shared_lock; +}; + /** * Wrapped mutex: supports recursive locking, but no waiting * TODO: We should move away from using the recursive lock by default. @@ -123,6 +139,9 @@ using RecursiveMutex = AnnotatedMixin; /** Wrapped mutex: supports waiting but not recursive locking */ typedef AnnotatedMixin Mutex; +/** Wrapped shared mutex: supports read locking via .shared_lock, exlusive locking via .lock; + * does not support recursive locking */ +typedef SharedAnnotatedMixin SharedMutex; #ifdef DEBUG_LOCKCONTENTION void PrintLockContention(const char* pszName, const char* pszFile, int nLine); @@ -223,16 +242,77 @@ public: friend class reverse_lock; }; +template +class SCOPED_LOCKABLE SharedLock : public Base +{ +private: + void SharedEnter(const char* pszName, const char* pszFile, int nLine) + { + EnterCritical(pszName, pszFile, nLine, Base::mutex()); +#ifdef DEBUG_LOCKCONTENTION + if (!Base::try_lock()) { + PrintLockContention(pszName, pszFile, nLine); +#endif + Base::lock(); +#ifdef DEBUG_LOCKCONTENTION + } +#endif + } + + bool TrySharedEnter(const char* pszName, const char* pszFile, int nLine) + { + EnterCritical(pszName, pszFile, nLine, Base::mutex(), true); + if (Base::try_lock()) { + return true; + } + LeaveCritical(); + return false; + } + +public: + SharedLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) SHARED_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock) + { + if (fTry) { + TrySharedEnter(pszName, pszFile, nLine); + } else { + SharedEnter(pszName, pszFile, nLine); + } + } + + SharedLock(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) SHARED_LOCK_FUNCTION(pmutexIn) + { + if (!pmutexIn) return; + + *static_cast(this) = Base(*pmutexIn, std::defer_lock); + if (fTry) { + TrySharedEnter(pszName, pszFile, nLine); + } else { + SharedEnter(pszName, pszFile, nLine); + } + } + + ~SharedLock() UNLOCK_FUNCTION() + { + if (Base::owns_lock()) { + LeaveCritical(); + } + } +}; + #define REVERSE_LOCK(g) typename std::decay::type::reverse_lock PASTE2(revlock, __COUNTER__)(g, #g, __FILE__, __LINE__) template using DebugLock = UniqueLock::type>::type>; +template +using ReadLock = SharedLock::type>::type>; #define LOCK(cs) DebugLock PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__) +#define READ_LOCK(cs) ReadLock PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__) #define LOCK2(cs1, cs2) \ DebugLock criticalblock1(cs1, #cs1, __FILE__, __LINE__); \ DebugLock criticalblock2(cs2, #cs2, __FILE__, __LINE__); #define TRY_LOCK(cs, name) DebugLock name(cs, #cs, __FILE__, __LINE__, true) +#define TRY_READ_LOCK(cs, name) ReadLock name(cs, #cs, __FILE__, __LINE__, true) #define WAIT_LOCK(cs, name) DebugLock name(cs, #cs, __FILE__, __LINE__) #define ENTER_CRITICAL_SECTION(cs) \ @@ -273,6 +353,7 @@ using DebugLock = UniqueLock decltype(auto) { LOCK(cs); code; }() +#define WITH_READ_LOCK(cs, code) [&]() -> decltype(auto) { READ_LOCK(cs); code; }() class CSemaphore {