From f5af41489071838c9d115943a4a302e6b72612e3 Mon Sep 17 00:00:00 2001 From: Casper Jeukendrup <48658420+cbjeukendrup@users.noreply.github.com> Date: Wed, 28 Dec 2022 20:34:30 +0100 Subject: [PATCH] WASAPI Driver: don't crash (or rather hang) when starting client fails We were waiting infinitely for the WASAPI_Client_Started event, but when there was an error while starting the client, that event would never be signaled. So MuseScore would be stuck infinitely on launch. --- .../internal/platform/win/wasapiaudioclient.cpp | 12 ++++++++++-- .../internal/platform/win/wasapiaudioclient.h | 3 ++- .../internal/platform/win/wasapiaudiodriver.cpp | 15 +++++++++++++-- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/framework/audio/internal/platform/win/wasapiaudioclient.cpp b/src/framework/audio/internal/platform/win/wasapiaudioclient.cpp index 2d05ab2f81806..1af58db0d3bd9 100644 --- a/src/framework/audio/internal/platform/win/wasapiaudioclient.cpp +++ b/src/framework/audio/internal/platform/win/wasapiaudioclient.cpp @@ -29,8 +29,9 @@ using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Media::Devices; using namespace winrt::Windows::Devices::Enumeration; -WasapiAudioClient::WasapiAudioClient(HANDLE clientStartedEvent, HANDLE clientStoppedEvent) - : m_clientStartedEvent(clientStartedEvent), m_clientStoppedEvent(clientStoppedEvent) +WasapiAudioClient::WasapiAudioClient(HANDLE clientStartedEvent, HANDLE clientFailedToStartEvent, HANDLE clientStoppedEvent) + : m_clientStartedEvent(clientStartedEvent), m_clientFailedToStartEvent(clientFailedToStartEvent), m_clientStoppedEvent( + clientStoppedEvent) { check_hresult(MFStartup(MF_VERSION, MFSTARTUP_LITE)); } @@ -218,6 +219,8 @@ HRESULT WasapiAudioClient::ActivateCompleted(IActivateAudioInterfaceAsyncOperati m_audioRenderClient = nullptr; m_sampleReadyAsyncResult = nullptr; + SetEvent(m_clientFailedToStartEvent); + // Must return S_OK even on failure. return S_OK; } @@ -370,6 +373,8 @@ void WasapiAudioClient::startPlayback() noexcept } catch (...) { hresult error = to_hresult(); setStateAndNotify(DeviceState::Error, error); + + SetEvent(m_clientFailedToStartEvent); } } @@ -398,6 +403,9 @@ HRESULT WasapiAudioClient::onStartPlayback(IMFAsyncResult*) noexcept return S_OK; } catch (...) { setStateAndNotify(DeviceState::Error, to_hresult()); + + SetEvent(m_clientFailedToStartEvent); + // Must return S_OK. return S_OK; } diff --git a/src/framework/audio/internal/platform/win/wasapiaudioclient.h b/src/framework/audio/internal/platform/win/wasapiaudioclient.h index 97993d2b0532b..4edab29a4a34c 100644 --- a/src/framework/audio/internal/platform/win/wasapiaudioclient.h +++ b/src/framework/audio/internal/platform/win/wasapiaudioclient.h @@ -29,7 +29,7 @@ namespace winrt { struct WasapiAudioClient : implements { public: - WasapiAudioClient(HANDLE clientStartedEvent, HANDLE clientStoppedEvent); + WasapiAudioClient(HANDLE clientStartedEvent, HANDLE clientFailedToStartEvent, HANDLE clientStoppedEvent); ~WasapiAudioClient(); void setHardWareOffload(bool value); @@ -99,6 +99,7 @@ struct WasapiAudioClient : implements wasapiClient; @@ -54,6 +55,7 @@ static WasapiData s_data; WasapiAudioDriver::WasapiAudioDriver() { s_data.clientStartedEvent = CreateEvent(NULL, FALSE, FALSE, L"WASAPI_Client_Started"); + s_data.clientFailedToStartEvent = CreateEvent(NULL, FALSE, FALSE, L"WASAPI_Client_Failed_To_Start"); s_data.clientStoppedEvent = CreateEvent(NULL, FALSE, FALSE, L"WASAPI_Client_Stopped"); m_devicesListener.startWithCallback([this]() { @@ -77,7 +79,8 @@ std::string WasapiAudioDriver::name() const bool WasapiAudioDriver::open(const Spec& spec, Spec* activeSpec) { if (!s_data.wasapiClient.get()) { - s_data.wasapiClient = make_self(s_data.clientStartedEvent, s_data.clientStoppedEvent); + s_data.wasapiClient + = make_self(s_data.clientStartedEvent, s_data.clientFailedToStartEvent, s_data.clientStoppedEvent); } m_desiredSpec = spec; @@ -102,7 +105,15 @@ bool WasapiAudioDriver::open(const Spec& spec, Spec* activeSpec) s_data.wasapiClient->asyncInitializeAudioDevice(deviceId); - WaitForSingleObject(s_data.clientStartedEvent, INFINITE); + static constexpr DWORD handleCount = 2; + const HANDLE handles[handleCount] = { s_data.clientStartedEvent, s_data.clientFailedToStartEvent }; + + DWORD waitResult = WaitForMultipleObjects(handleCount, handles, false, INFINITE); + if (waitResult != WAIT_OBJECT_0) { + // Either the event was the second event (namely s_data.clientFailedToStartEvent) + // Or some wait error occurred + return false; + } m_activeSpec = m_desiredSpec; m_activeSpec.sampleRate = s_data.wasapiClient->sampleRate();