diff options
| author | Admiral H. Curtiss <pikachu025@gmail.com> | 2022-06-18 00:39:41 +0200 |
|---|---|---|
| committer | Admiral H. Curtiss <pikachu025@gmail.com> | 2022-11-26 05:05:56 +0100 |
| commit | ca10e92ab92afeb4cb6d3ac935cf86767ab469eb (patch) | |
| tree | 4830494d6220750b7c0bf1187a22fdf2d58e6d4f /Source/Core/AudioCommon/CubebUtils.cpp | |
| parent | 59b1419a8ef2912f349c1bd3984bc85cc21c6ff1 (diff) | |
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 <mchtly@gmail.com>
Diffstat (limited to 'Source/Core/AudioCommon/CubebUtils.cpp')
| -rw-r--r-- | Source/Core/AudioCommon/CubebUtils.cpp | 75 |
1 files changed, 75 insertions, 0 deletions
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 <cstdarg> #include <cstddef> #include <cstring> +#include <thread> +#include "Common/Assert.h" #include "Common/CommonPaths.h" #include "Common/Logging/Log.h" #include "Common/Logging/LogManager.h" @@ -14,6 +16,49 @@ #include <cubeb/cubeb.h> +#ifdef _WIN32 +#include <Objbase.h> +#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<void()>& 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<cubeb> CubebUtils::GetContext() { + if (!EnsureCubebCallable()) + return nullptr; + static std::weak_ptr<cubeb> weak; std::shared_ptr<cubeb> shared = weak.lock(); |
