summaryrefslogtreecommitdiff
path: root/Source/Core/Common
diff options
context:
space:
mode:
authorJordan Woyak <jordan.woyak@gmail.com>2026-02-03 16:50:52 -0600
committerGitHub <noreply@github.com>2026-02-03 16:50:52 -0600
commitdd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f (patch)
tree5355c7878fc942b9b9ed79f2b9ca09923c5a8557 /Source/Core/Common
parenteac7b290423efea867c2c7f3f2b2792d05418bdd (diff)
parent2322437f96bc53f1edcd9100daedb5b54c6e0fc4 (diff)
Merge pull request #13594 from jordan-woyak/state-cleanups
State: Simplify interthread communication and general cleanups.
Diffstat (limited to 'Source/Core/Common')
-rw-r--r--Source/Core/Common/CMakeLists.txt1
-rw-r--r--Source/Core/Common/TransferableSharedMutex.h92
2 files changed, 93 insertions, 0 deletions
diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt
index 5ef6e8527f..fac5900f76 100644
--- a/Source/Core/Common/CMakeLists.txt
+++ b/Source/Core/Common/CMakeLists.txt
@@ -151,6 +151,7 @@ add_library(common
Timer.h
TimeUtil.cpp
TimeUtil.h
+ TransferableSharedMutex.h
TraversalClient.cpp
TraversalClient.h
TraversalProto.h
diff --git a/Source/Core/Common/TransferableSharedMutex.h b/Source/Core/Common/TransferableSharedMutex.h
new file mode 100644
index 0000000000..f735046ff0
--- /dev/null
+++ b/Source/Core/Common/TransferableSharedMutex.h
@@ -0,0 +1,92 @@
+// Copyright 2025 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <atomic>
+#include <cassert>
+#include <cstdint>
+
+namespace Common
+{
+// Behaves like `std::shared_mutex` but locks and unlocks may come from different threads.
+class TransferableSharedMutex
+{
+public:
+ void lock()
+ {
+ while (true)
+ {
+ CounterType old_value{};
+ if (m_counter.compare_exchange_strong(old_value, EXCLUSIVE_LOCK_VALUE,
+ std::memory_order_acquire, std::memory_order_relaxed))
+ {
+ return;
+ }
+
+ // lock() or lock_shared() is already held.
+ // Wait for an unlock notification and try again.
+ m_counter.wait(old_value, std::memory_order_relaxed);
+ }
+ }
+
+ bool try_lock()
+ {
+ CounterType old_value{};
+ return m_counter.compare_exchange_weak(old_value, EXCLUSIVE_LOCK_VALUE,
+ std::memory_order_acquire, std::memory_order_relaxed);
+ }
+
+ void unlock()
+ {
+ m_counter.store(0, std::memory_order_release);
+ m_counter.notify_all(); // Notify potentially multiple wait()ers in lock_shared().
+ }
+
+ void lock_shared()
+ {
+ while (true)
+ {
+ auto old_value = m_counter.load(std::memory_order_relaxed);
+ while (old_value < LAST_SHARED_LOCK_VALUE)
+ {
+ if (m_counter.compare_exchange_strong(old_value, old_value + 1, std::memory_order_acquire,
+ std::memory_order_relaxed))
+ {
+ return;
+ }
+ }
+
+ // Something has gone very wrong if m_counter is nearly saturated with shared_lock().
+ assert(old_value != LAST_SHARED_LOCK_VALUE);
+
+ // lock() is already held.
+ // Wait for an unlock notification and try again.
+ m_counter.wait(old_value, std::memory_order_relaxed);
+ }
+ }
+
+ bool try_lock_shared()
+ {
+ auto old_value = m_counter.load(std::memory_order_relaxed);
+ return (old_value < LAST_SHARED_LOCK_VALUE) &&
+ m_counter.compare_exchange_weak(old_value, old_value + 1, std::memory_order_acquire,
+ std::memory_order_relaxed);
+ }
+
+ void unlock_shared()
+ {
+ if (m_counter.fetch_sub(1, std::memory_order_release) == 1)
+ m_counter.notify_one(); // Notify one of the wait()ers in lock().
+ }
+
+private:
+ using CounterType = std::uintptr_t;
+
+ static constexpr auto EXCLUSIVE_LOCK_VALUE = CounterType(-1);
+ static constexpr auto LAST_SHARED_LOCK_VALUE = EXCLUSIVE_LOCK_VALUE - 1;
+
+ std::atomic<CounterType> m_counter{};
+};
+
+} // namespace Common