diff options
author | Liam <byteslice@airmail.cc> | 2024-01-16 23:35:48 -0500 |
---|---|---|
committer | Fernando Sahmkow <fsahmkow27@gmail.com> | 2024-01-31 16:38:51 +0100 |
commit | 8f848f43e979ac4049237d3b6a161696dd85372b (patch) | |
tree | e218fcb939560362b4293ab035669d776d6e0cb0 /src/common/range_mutex.h | |
parent | a7c1306e2d19f1270f6fa0603ba20043c90e9c05 (diff) |
smmu: use new range mutex construction for protecting counters
Diffstat (limited to 'src/common/range_mutex.h')
-rw-r--r-- | src/common/range_mutex.h | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/src/common/range_mutex.h b/src/common/range_mutex.h new file mode 100644 index 000000000..d6c949811 --- /dev/null +++ b/src/common/range_mutex.h @@ -0,0 +1,93 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <condition_variable> +#include <mutex> + +#include "common/intrusive_list.h" + +namespace Common { + +class ScopedRangeLock; + +class RangeMutex { +public: + explicit RangeMutex() = default; + ~RangeMutex() = default; + +private: + friend class ScopedRangeLock; + + void Lock(ScopedRangeLock& l); + void Unlock(ScopedRangeLock& l); + bool HasIntersectionLocked(ScopedRangeLock& l); + +private: + std::mutex m_mutex; + std::condition_variable m_cv; + + using LockList = Common::IntrusiveListBaseTraits<ScopedRangeLock>::ListType; + LockList m_list; +}; + +class ScopedRangeLock : public Common::IntrusiveListBaseNode<ScopedRangeLock> { +public: + explicit ScopedRangeLock(RangeMutex& mutex, u64 address, u64 size) + : m_mutex(mutex), m_address(address), m_size(size) { + if (m_size > 0) { + m_mutex.Lock(*this); + } + } + ~ScopedRangeLock() { + if (m_size > 0) { + m_mutex.Unlock(*this); + } + } + + u64 GetAddress() const { + return m_address; + } + + u64 GetSize() const { + return m_size; + } + +private: + RangeMutex& m_mutex; + const u64 m_address{}; + const u64 m_size{}; +}; + +inline void RangeMutex::Lock(ScopedRangeLock& l) { + std::unique_lock lk{m_mutex}; + m_cv.wait(lk, [&] { return !HasIntersectionLocked(l); }); + m_list.push_back(l); +} + +inline void RangeMutex::Unlock(ScopedRangeLock& l) { + { + std::scoped_lock lk{m_mutex}; + m_list.erase(m_list.iterator_to(l)); + } + m_cv.notify_all(); +} + +inline bool RangeMutex::HasIntersectionLocked(ScopedRangeLock& l) { + const auto cur_begin = l.GetAddress(); + const auto cur_last = l.GetAddress() + l.GetSize() - 1; + + for (const auto& other : m_list) { + const auto other_begin = other.GetAddress(); + const auto other_last = other.GetAddress() + other.GetSize() - 1; + + if (cur_begin <= other_last && other_begin <= cur_last) { + return true; + } + } + + return false; +} + +} // namespace Common |