diff options
| author | Jordan Woyak <jordan.woyak@gmail.com> | 2026-02-03 16:50:52 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-02-03 16:50:52 -0600 |
| commit | dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f (patch) | |
| tree | 5355c7878fc942b9b9ed79f2b9ca09923c5a8557 /Source/Core/Common | |
| parent | eac7b290423efea867c2c7f3f2b2792d05418bdd (diff) | |
| parent | 2322437f96bc53f1edcd9100daedb5b54c6e0fc4 (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.txt | 1 | ||||
| -rw-r--r-- | Source/Core/Common/TransferableSharedMutex.h | 92 |
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 |
