From 6613efc3b264232dd9e14e8068665e5d7a1d6b23 Mon Sep 17 00:00:00 2001 From: Persune <54422576+Gumball2415@users.noreply.github.com> Date: Wed, 27 Dec 2023 13:59:01 +0800 Subject: [PATCH] Switch to fair mutexes to avoid UI deadlocks (#245) Taken from https://github.com/yohhoy/yamc/blob/master/include/fair_mutex.hpp Co-authored-by: nyanpasu64 --- Source/SoundGen.h | 17 +++++--- Source/yamc/fair_mutex.hpp | 87 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 Source/yamc/fair_mutex.hpp diff --git a/Source/SoundGen.h b/Source/SoundGen.h index fdcb3059..c87f5f00 100644 --- a/Source/SoundGen.h +++ b/Source/SoundGen.h @@ -32,6 +32,7 @@ #include "rigtorp/SPSCQueue.h" #include "libsamplerate/include/samplerate.h" #include "utils/handle_ptr.h" +#include "yamc/fair_mutex.hpp" #include // // // #include "Common.h" #include "FamiTrackerTypes.h" @@ -39,7 +40,6 @@ #include #include #include -#include #include const int VIBRATO_LENGTH = 256; @@ -107,6 +107,8 @@ class CRegisterState; // // // // CSoundGen +using FairMutex = yamc::fair::mutex; + class CSoundGen : IAudioCallback { public: @@ -369,7 +371,10 @@ class CSoundGen : IAudioCallback // Thread synchronization private: - mutable std::mutex m_csAPULock; // // // + /// A fair mutex ensures that if the audio thread holds m_csAPULock, + /// and the UI thread is waiting for m_csAPULock, + /// the audio thread cannot release and reacquire m_csAPULock (starving the UI thread of access). + mutable FairMutex m_csAPULock; // // // mutable std::mutex m_csVisualizerWndLock; // Handles @@ -513,11 +518,11 @@ class CSoundGen : IAudioCallback afx_msg void OnRemoveDocument(WPARAM wParam, LPARAM lParam); public: - std::unique_lock Lock() { - return std::unique_lock(m_csAPULock); + std::unique_lock Lock() { + return std::unique_lock(m_csAPULock); } - std::unique_lock DeferLock() { - return std::unique_lock(m_csAPULock, std::defer_lock); + std::unique_lock DeferLock() { + return std::unique_lock(m_csAPULock, std::defer_lock); } }; diff --git a/Source/yamc/fair_mutex.hpp b/Source/yamc/fair_mutex.hpp new file mode 100644 index 00000000..b3b72abd --- /dev/null +++ b/Source/yamc/fair_mutex.hpp @@ -0,0 +1,87 @@ +/* + * fair_mutex.hpp + * + * MIT License + * + * Copyright (c) 2017 yohhoy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef YAMC_FAIR_MUTEX_HPP_ +#define YAMC_FAIR_MUTEX_HPP_ + +#include +#include + + +namespace yamc { + +/* + * fairness (FIFO locking) mutex + * + * - yamc::fair::mutex + * - yamc::fair::recursive_mutex + * - yamc::fair::timed_mutex + * - yamc::fair::recursive_timed_mutex + */ +namespace fair { + +class mutex { + std::size_t next_ = 0; + std::size_t curr_ = 0; + std::condition_variable cv_; + std::mutex mtx_; + +public: + mutex() = default; + ~mutex() = default; + + mutex(const mutex&) = delete; + mutex& operator=(const mutex&) = delete; + + void lock() + { + std::unique_lock lk(mtx_); + const std::size_t request = next_++; + while (request != curr_) { + cv_.wait(lk); + } + } + + bool try_lock() + { + std::lock_guard lk(mtx_); + if (next_ != curr_) + return false; + ++next_; + return true; + } + + void unlock() + { + std::lock_guard lk(mtx_); + ++curr_; + cv_.notify_all(); + } +}; + +} // namespace fair +} // namespace yamc + +#endif