diff --git a/Utilities/mutex.cpp b/Utilities/mutex.cpp index b2740c3186..9937212670 100644 --- a/Utilities/mutex.cpp +++ b/Utilities/mutex.cpp @@ -1,7 +1,12 @@ #include "mutex.h" #include "sync.h" -#include +#include +#include +#include + +// TLS variable for tracking owned mutexes +thread_local std::vector g_tls_locks; void shared_mutex::imp_lock_shared(s64 _old) { @@ -217,12 +222,14 @@ void shared_mutex::imp_unlock(s64 _old) void shared_mutex::imp_lock_upgrade() { + // TODO unlock_shared(); lock(); } void shared_mutex::imp_lock_degrade() { + // TODO unlock(); lock_shared(); } @@ -250,3 +257,78 @@ bool shared_mutex::try_lock_degrade() // TODO return m_value.compare_and_swap_test(0, c_one - c_min); } + +safe_reader_lock::safe_reader_lock(shared_mutex& mutex) + : m_mutex(mutex) + , m_is_owned(false) +{ + if (std::count(g_tls_locks.cbegin(), g_tls_locks.cend(), &m_mutex) == 0) + { + m_is_owned = true; + + if (m_is_owned) + { + m_mutex.lock_shared(); + g_tls_locks.emplace_back(&m_mutex); + return; + } + + // TODO: order locks + } +} + +safe_reader_lock::~safe_reader_lock() +{ + if (m_is_owned) + { + m_mutex.unlock_shared(); + g_tls_locks.erase(std::remove(g_tls_locks.begin(), g_tls_locks.end(), &m_mutex), g_tls_locks.cend()); + return; + } + + // TODO: order locks +} + +safe_writer_lock::safe_writer_lock(shared_mutex& mutex) + : m_mutex(mutex) + , m_is_owned(false) + , m_is_upgraded(false) +{ + if (std::count(g_tls_locks.cbegin(), g_tls_locks.cend(), &m_mutex) == 0) + { + m_is_owned = true; + + if (m_is_owned) + { + m_mutex.lock_shared(); + g_tls_locks.emplace_back(&m_mutex); + return; + } + + // TODO: order locks + } + + if (m_mutex.is_reading()) + { + m_is_upgraded = true; + m_mutex.lock_upgrade(); + } +} + +safe_writer_lock::~safe_writer_lock() +{ + if (m_is_upgraded) + { + m_mutex.lock_degrade(); + return; + } + + if (m_is_owned) + { + m_mutex.unlock(); + g_tls_locks.erase(std::remove(g_tls_locks.begin(), g_tls_locks.end(), &m_mutex), g_tls_locks.cend()); + return; + } + + // TODO: order locks +} diff --git a/Utilities/mutex.h b/Utilities/mutex.h index 9add90ff79..235b36dfec 100644 --- a/Utilities/mutex.h +++ b/Utilities/mutex.h @@ -95,6 +95,11 @@ public: imp_lock_degrade(); } } + + bool is_reading() const + { + return (m_value.load() % c_one) != 0; + } }; // Simplified shared (reader) lock implementation. @@ -171,3 +176,56 @@ public: unlock(); } }; + +// Safe reader lock. Can be recursive above other safe locks (reader or writer). +class safe_reader_lock final +{ + shared_mutex& m_mutex; + bool m_is_owned; + + void lock() + { + m_mutex.lock_shared(); + } + + void unlock() + { + m_mutex.unlock_shared(); + } + + friend class cond_variable; + +public: + safe_reader_lock(const safe_reader_lock&) = delete; + + explicit safe_reader_lock(shared_mutex& mutex); + + ~safe_reader_lock(); +}; + +// Safe writer lock. Can be recursive above other safe locks. Performs upgrade and degrade operations above existing reader lock if necessary. +class safe_writer_lock final +{ + shared_mutex& m_mutex; + bool m_is_owned; + bool m_is_upgraded; + + void lock() + { + m_mutex.lock(); + } + + void unlock() + { + m_mutex.unlock(); + } + + friend class cond_variable; + +public: + safe_writer_lock(const safe_writer_lock&) = delete; + + explicit safe_writer_lock(shared_mutex& mutex); + + ~safe_writer_lock(); +};