From ca10e92ab92afeb4cb6d3ac935cf86767ab469eb Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Sat, 18 Jun 2022 00:39:41 +0200 Subject: Core: Abide by COM MTA requirement for cubeb on Windows. Partially based on https://github.com/dolphin-emu/dolphin/pull/8920#discussion_r459746604 Co-authored-by: Michael M --- Source/Core/AudioCommon/CubebUtils.cpp | 75 ++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) (limited to 'Source/Core/AudioCommon/CubebUtils.cpp') diff --git a/Source/Core/AudioCommon/CubebUtils.cpp b/Source/Core/AudioCommon/CubebUtils.cpp index e4050a8bf5..3aff62cecf 100644 --- a/Source/Core/AudioCommon/CubebUtils.cpp +++ b/Source/Core/AudioCommon/CubebUtils.cpp @@ -6,7 +6,9 @@ #include #include #include +#include +#include "Common/Assert.h" #include "Common/CommonPaths.h" #include "Common/Logging/Log.h" #include "Common/Logging/LogManager.h" @@ -14,6 +16,49 @@ #include +#ifdef _WIN32 +#include +#endif + +// On Windows, we must manually ensure that COM is initialized in MTA mode on every thread that +// accesses the cubeb API. See the comment on cubeb_init in cubeb.h +// We do this with a thread-local variable that keeps track of whether COM is initialized or not, +// and initialize it if it isn't. When the thread ends COM is uninitialized again. +#ifdef _WIN32 +namespace +{ +class auto_com +{ +public: + auto_com() = default; + auto_com(const auto_com&) = delete; + auto_com(auto_com&&) = delete; + auto_com& operator=(const auto_com&) = delete; + auto_com& operator=(auto_com&&) = delete; + ~auto_com() + { + if (m_initialized) + { + CoUninitialize(); + } + } + bool initialize() + { + if (!m_initialized) + { + HRESULT result = CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); + m_initialized = SUCCEEDED(result); + } + return m_initialized; + } + +private: + bool m_initialized = false; +}; +} // namespace +static thread_local auto_com tls_com_context; +#endif + static ptrdiff_t s_path_cutoff_point = 0; static void LogCallback(const char* format, ...) @@ -47,8 +92,38 @@ static void DestroyContext(cubeb* ctx) } } +static bool EnsureCubebCallable() +{ +#ifdef _WIN32 + if (!tls_com_context.initialize()) + return false; +#endif + return true; +} + +void CubebUtils::RunInCubebContext(const std::function& func) +{ + // Cubeb is documented to require MTA COM mode, so if the current thread was initialized in STA + // mode, we make a temporary thread to execute the cubeb call. + if (EnsureCubebCallable()) + { + func(); + } + else + { + std::thread([&] { + // this should never fail, so yell loudly if it does + ASSERT(EnsureCubebCallable()); + func(); + }).join(); + } +} + std::shared_ptr CubebUtils::GetContext() { + if (!EnsureCubebCallable()) + return nullptr; + static std::weak_ptr weak; std::shared_ptr shared = weak.lock(); -- cgit v1.2.3 From e085bf14f99c52eff514e2317af740869691ba82 Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Sat, 18 Jun 2022 05:31:14 +0200 Subject: Core: Use extra thread for Cubeb on Windows to not disturb the CoInitialize state of whatever thread happens to call a Cubeb function. --- Source/Core/AudioCommon/CubebUtils.cpp | 73 ---------------------------------- 1 file changed, 73 deletions(-) (limited to 'Source/Core/AudioCommon/CubebUtils.cpp') diff --git a/Source/Core/AudioCommon/CubebUtils.cpp b/Source/Core/AudioCommon/CubebUtils.cpp index 3aff62cecf..f7682f6ade 100644 --- a/Source/Core/AudioCommon/CubebUtils.cpp +++ b/Source/Core/AudioCommon/CubebUtils.cpp @@ -16,49 +16,6 @@ #include -#ifdef _WIN32 -#include -#endif - -// On Windows, we must manually ensure that COM is initialized in MTA mode on every thread that -// accesses the cubeb API. See the comment on cubeb_init in cubeb.h -// We do this with a thread-local variable that keeps track of whether COM is initialized or not, -// and initialize it if it isn't. When the thread ends COM is uninitialized again. -#ifdef _WIN32 -namespace -{ -class auto_com -{ -public: - auto_com() = default; - auto_com(const auto_com&) = delete; - auto_com(auto_com&&) = delete; - auto_com& operator=(const auto_com&) = delete; - auto_com& operator=(auto_com&&) = delete; - ~auto_com() - { - if (m_initialized) - { - CoUninitialize(); - } - } - bool initialize() - { - if (!m_initialized) - { - HRESULT result = CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); - m_initialized = SUCCEEDED(result); - } - return m_initialized; - } - -private: - bool m_initialized = false; -}; -} // namespace -static thread_local auto_com tls_com_context; -#endif - static ptrdiff_t s_path_cutoff_point = 0; static void LogCallback(const char* format, ...) @@ -92,38 +49,8 @@ static void DestroyContext(cubeb* ctx) } } -static bool EnsureCubebCallable() -{ -#ifdef _WIN32 - if (!tls_com_context.initialize()) - return false; -#endif - return true; -} - -void CubebUtils::RunInCubebContext(const std::function& func) -{ - // Cubeb is documented to require MTA COM mode, so if the current thread was initialized in STA - // mode, we make a temporary thread to execute the cubeb call. - if (EnsureCubebCallable()) - { - func(); - } - else - { - std::thread([&] { - // this should never fail, so yell loudly if it does - ASSERT(EnsureCubebCallable()); - func(); - }).join(); - } -} - std::shared_ptr CubebUtils::GetContext() { - if (!EnsureCubebCallable()) - return nullptr; - static std::weak_ptr weak; std::shared_ptr shared = weak.lock(); -- cgit v1.2.3