diff --git a/indra/llaudio/llaudioengine.h b/indra/llaudio/llaudioengine.h
index 2a2355b019..aa0617f936 100644
--- a/indra/llaudio/llaudioengine.h
+++ b/indra/llaudio/llaudioengine.h
@@ -39,6 +39,10 @@
#include "llframetimer.h"
#include "llassettype.h"
#include "llextendedstatus.h"
+// [FIRE-36022] - Removing my USB headset crashes entire viewer
+// Need to include for audio device mutex shared with other audio/voice systems.
+#include "inlinemutexs.h"
+// [FIRE-36022]
#include "lllistener.h"
diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp
index e9996b93e0..7daaea1c76 100644
--- a/indra/llaudio/llaudioengine_fmodstudio.cpp
+++ b/indra/llaudio/llaudioengine_fmodstudio.cpp
@@ -44,6 +44,9 @@
#include "sound_ids.h"
+// [FIRE-36022] - Removing my USB headset crashes entire viewer
+using namespace std::chrono_literals; // Needed for shared timed mutex to use time
+// [FIRE-36022]
constexpr U32 EXTRA_SOUND_CHANNELS = 10;
FMOD_RESULT F_CALL windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int *outchannels);
@@ -113,8 +116,14 @@ static void set_device(FMOD::System* system, const LLUUID& device_uuid)
}
}
-FMOD_RESULT F_CALL systemCallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACK_TYPE type, void *commanddata1, void *commanddata2, void* userdata)
+// [FIRE-36022] - Removing my USB headset crashes entire viewer
+// According to FMOD, not having this method static is very bad.
+//FMOD_RESULT F_CALL systemCallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACK_TYPE type, void *commanddata1, void *commanddata2, void* userdata)
+static FMOD_RESULT F_CALL systemCallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACK_TYPE type, void *commanddata1, void *commanddata2, void* userdata)
{
+ try // Try catch needed for uniquie lock as will throw an exception if a second lock is attempted or the mutex is invalid
+ {
+// [FIRE-36022]
FMOD::System* sys = (FMOD::System*)system;
LLAudioEngine_FMODSTUDIO* audio_engine = (LLAudioEngine_FMODSTUDIO*)userdata;
@@ -124,6 +133,17 @@ FMOD_RESULT F_CALL systemCallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACK_TYPE
LL_DEBUGS("FMOD") << "FMOD system callback FMOD_SYSTEM_CALLBACK_DEVICELISTCHANGED" << LL_ENDL;
if (sys && audio_engine)
{
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ // Attempt to lock the access to the audio device, wait up to 1 second for other threads to unlock.
+ std::unique_lock lock(gAudioDeviceMutex, 1s);
+ // If the lock could not be accessed, return as we don't have hardware access and will need to try again another pass.
+ // Prevents threads from interacting with the hardware at the same time as other audio/voice threads.
+ if (!lock.owns_lock())
+ {
+ LL_INFOS() << "Could not access the audio device mutex, trying again later" << LL_ENDL;
+ return FMOD_OK; // Could be a FMOD_ERR_ALREADY_LOCKED;
+ }
+ // [FIRE-36022]
set_device(sys, audio_engine->getSelectedDeviceUUID());
audio_engine->OnOutputDeviceListChanged(audio_engine->getDevices());
}
@@ -132,6 +152,34 @@ FMOD_RESULT F_CALL systemCallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACK_TYPE
break;
}
return FMOD_OK;
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ }
+ // There are two exceptions that unique_lock can trigger, operation_not_permitted or resource_deadlock_would_occur
+ catch (const std::system_error& e)
+ {
+ if (e.code() == std::errc::resource_deadlock_would_occur)
+ {
+ LL_WARNS() << "Exception FMOD: " << e.code() << " " << e.what() << LL_ENDL;
+ return FMOD_ERR_ALREADY_LOCKED;
+ }
+ else if (e.code() == std::errc::operation_not_permitted)
+ {
+ LL_WARNS() << "Exception FMOD: " << e.code() << " " << e.what() << LL_ENDL;
+ return FMOD_ERR_ALREADY_LOCKED;
+ }
+ else
+ {
+ LL_WARNS() << "Exception FMOD: " << e.code() << " " << e.what() << LL_ENDL;
+ }
+
+ return FMOD_ERR_ALREADY_LOCKED;
+ }
+ catch (const std::exception& e)
+ {
+ LL_WARNS() << "Exception FMOD: " << " " << e.what() << LL_ENDL;
+ return FMOD_ERR_ALREADY_LOCKED;
+ }
+ // [FIRE-36022]
}
LLAudioEngine_FMODSTUDIO::LLAudioEngine_FMODSTUDIO(bool enable_profiler, U32 resample_method)
@@ -311,6 +359,11 @@ bool LLAudioEngine_FMODSTUDIO::init(void* userdata, const std::string &app_title
LL_INFOS("AppInit") << "LLAudioEngine_FMODSTUDIO::init() FMOD Studio initialized correctly" << LL_ENDL;
FMOD_ADVANCEDSETTINGS settings_dump = { };
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ // With the FMOD debug library used, turns out this object needs to have a size assigned to it otherwise it will fail.
+ // So the viewer never got any advanced settings for the info below.
+ settings_dump.cbSize = sizeof(FMOD_ADVANCEDSETTINGS);
+ // [FIRE-36022]
mSystem->getAdvancedSettings(&settings_dump);
LL_INFOS("AppInit") << "LLAudioEngine_FMODSTUDIO::init(): resampler=" << settings_dump.resamplerMethod << " bytes" << LL_ENDL;
@@ -373,7 +426,47 @@ LLAudioEngine_FMODSTUDIO::output_device_map_t LLAudioEngine_FMODSTUDIO::getDevic
void LLAudioEngine_FMODSTUDIO::setDevice(const LLUUID& device_uuid)
{
mSelectedDeviceUUID = device_uuid;
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ try // Try catch needed for uniquie lock as will throw an exception if a second lock is attempted or the mutex is invalid
+ {
+ // Attempt to lock the access to the audio device, wait up to 1 second for other threads to unlock.
+ std::unique_lock lock(gAudioDeviceMutex, 1s);
+ // If the lock could not be accessed, return as we don't have hardware access and will need to try again another pass.
+ // Prevents threads from interacting with the hardware at the same time as other audio/voice threads.
+ if (!lock.owns_lock())
+ {
+ LL_INFOS() << "Could not access the audio device mutex, trying again later" << LL_ENDL;
+ return;
+ }
+ // [FIRE-36022]
set_device(mSystem, device_uuid);
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ }
+ // There are two exceptions that unique_lock can trigger, operation_not_permitted or resource_deadlock_would_occur
+ catch (const std::system_error& e)
+ {
+ if (e.code() == std::errc::resource_deadlock_would_occur)
+ {
+ LL_WARNS() << "Exception FMOD: " << e.code() << " " << e.what() << LL_ENDL;
+ }
+ else if (e.code() == std::errc::operation_not_permitted)
+ {
+ LL_WARNS() << "Exception FMOD: " << e.code() << " " << e.what() << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS() << "Exception FMOD: " << e.code() << " " << e.what() << LL_ENDL;
+ }
+
+ return;
+ }
+ catch (const std::exception& e)
+ {
+ LL_WARNS() << "Exception FMOD: " << " " << e.what() << LL_ENDL;
+ return;
+ }
+ // [FIRE-36022]
+
}
std::string LLAudioEngine_FMODSTUDIO::getDriverName(bool verbose)
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 0d88dec885..1ba315b11a 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -122,6 +122,7 @@ set(llcommon_HEADER_FILES
fsyspath.h
function_types.h
indra_constants.h
+ inlinemutexs.h
lazyeventapi.h
linden_common.h
llalignedarray.h
diff --git a/indra/llcommon/inlinemutexs.h b/indra/llcommon/inlinemutexs.h
new file mode 100644
index 0000000000..3b8ab5c4ef
--- /dev/null
+++ b/indra/llcommon/inlinemutexs.h
@@ -0,0 +1,39 @@
+/**
+* @file inlinemutexs.h
+* @brief Declaration of inline mutexs
+* @author minerjr@firestorm
+*
+ * $LicenseInfo:firstyear=2025&license=fsviewerlgpl$
+ * Phoenix Firestorm Viewer Source Code
+ * Copyright (C) 2025, Minerjr
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
+ * http://www.firestormviewer.org
+ * $/LicenseInfo$
+*/
+
+#ifndef INLINE_MUTEXS_HEADER
+#define INLINE_MUTEXS_HEADER
+#include
+#include
+#include
+
+// Audio device mutex to be shared between audio engine and Voice systems to
+// syncronize on when audio hardware accessed for disconnected/connecting hardware
+// Uses Timed Mutex so as to not lockup the threads forever.
+inline std::timed_mutex gAudioDeviceMutex;
+#endif
diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp
index d686082878..860f619885 100644
--- a/indra/llwebrtc/llwebrtc.cpp
+++ b/indra/llwebrtc/llwebrtc.cpp
@@ -27,6 +27,11 @@
#include "llwebrtc_impl.h"
#include
#include
+// [FIRE-36022] - Removing my USB headset crashes entire viewer
+// Needed for accessing the inline timed mutex for accessing audio hardware.
+#include
+#include
+// [FIRE-36022]
#include "api/audio_codecs/audio_decoder_factory.h"
#include "api/audio_codecs/audio_encoder_factory.h"
@@ -39,6 +44,14 @@
#include "modules/audio_mixer/audio_mixer_impl.h"
#include "api/environment/environment_factory.h"
+// [FIRE-36022] - Removing my USB headset crashes entire viewer
+// Audio device mutex to be shared between audio engine and Voice systems to
+// syncronize on when audio hardware accessed for disconnected/connecting hardware
+// Uses Timed Mutex so as to not lockup the threads forever.
+inline std::timed_mutex gAudioDeviceMutex;
+// Need to use to access the 3 second timeout for the lock.
+using namespace std::chrono_literals;
+// [FIRE-36022]
namespace llwebrtc
{
#if WEBRTC_WIN
@@ -269,7 +282,11 @@ void LLWebRTCImpl::init()
webrtc::InitializeSSL();
// Normal logging is rather spammy, so turn it off.
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ // Turn on more verbose logging as we are looking for crashes.
webrtc::LogMessage::LogToDebug(webrtc::LS_NONE);
+ //webrtc::LogMessage::LogToDebug(webrtc::LS_VERBOSE);
+ // [FIRE-36022]
webrtc::LogMessage::SetLogToStderr(true);
webrtc::LogMessage::AddLogToStream(mLogSink, webrtc::LS_VERBOSE);
@@ -457,8 +474,24 @@ void LLWebRTCImpl::unsetDevicesObserver(LLWebRTCDevicesObserver *observer)
// must be run in the worker thread.
void LLWebRTCImpl::workerDeployDevices()
{
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ try // Try catch needed for uniquie lock as will throw an exception if a second lock is attempted or the mutex is invalid
+ {
+ // Attempt to lock the access to the audio device, wait up to 1 second for other threads to unlock.
+ std::unique_lock lock(gAudioDeviceMutex, 1s);
+ // If the lock could not be accessed, return as we don't have hardware access and will need to try again another pass.
+ // Prevents threads from interacting with the hardware at the same time as other audio/voice threads.
+ if (!lock.owns_lock())
+ {
+ return;
+ }
+ // [FIRE-36022]
if (!mDeviceModule)
{
+ // [FIRE-36022]
+ // If the device is not avaiable, then make sure the flag for the WebRTC updated devices flag is turned off for the co-routine
+ gWebRTCUpdateDevices = false;
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
return;
}
@@ -483,6 +516,10 @@ void LLWebRTCImpl::workerDeployDevices()
}
}
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ // Flag the device is being interacted with for the Co-routine in case something goes wrong.
+ gWebRTCUpdateDevices = true;
+ // [FIRE-36022]
mDeviceModule->StopPlayout();
mDeviceModule->ForceStopRecording();
#if WEBRTC_WIN
@@ -546,6 +583,10 @@ void LLWebRTCImpl::workerDeployDevices()
{
mDeviceModule->StartPlayout();
}
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ // Finally signal to the co-routine everyting is OK.
+ gWebRTCUpdateDevices = false;
+ // [FIRE-36022]
mSignalingThread->PostTask(
[this]
{
@@ -566,6 +607,38 @@ void LLWebRTCImpl::workerDeployDevices()
mWorkerThread->PostTask([this] { workerDeployDevices(); });
}
});
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ }
+ // There are two exceptions that unique_lock can trigger, operation_not_permitted or resource_deadlock_would_occur
+ catch (const std::system_error& e)
+ {
+ if (e.code() == std::errc::resource_deadlock_would_occur)
+ {
+ // Another thead may have alreayd called this method
+ mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
+ }
+ else if (e.code() == std::errc::operation_not_permitted)
+ {
+ // This should not be reached
+ mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
+ }
+ else
+ {
+ // Log any other message
+ mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
+ }
+ // Device no longer being interacted with
+ gWebRTCUpdateDevices = false;
+ return;
+ }
+ catch (const std::exception& e)
+ {
+ mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
+ // Device no longer being interacted with
+ gWebRTCUpdateDevices = false;
+ return;
+ }
+ // [FIRE-36022]
}
void LLWebRTCImpl::setCaptureDevice(const std::string &id)
@@ -584,11 +657,27 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id)
// updateDevices needs to happen on the worker thread.
void LLWebRTCImpl::updateDevices()
{
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ try // Try catch needed for uniquie lock as will throw an exception if a second lock is attempted or the mutex is invalid
+ {
+ // Attempt to lock the access to the audio device, wait up to 1 second for other threads to unlock.
+ std::unique_lock lock(gAudioDeviceMutex, 1s);
+ // If the lock could not be accessed, return as we don't have hardware access and will need to try again another pass.
+ // Prevents threads from interacting with the hardware at the same time as other audio/voice threads.
+ if (!lock.owns_lock())
+ {
+ return;
+ }
+ // [FIRE-36022]
if (!mDeviceModule)
{
return;
}
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ // Flag the device is being interacted with for the Co-routine in case something goes wrong.
+ gWebRTCUpdateDevices = true;
+ // [FIRE-36022]
int16_t renderDeviceCount = mDeviceModule->PlayoutDevices();
mPlayoutDeviceList.clear();
@@ -625,10 +714,46 @@ void LLWebRTCImpl::updateDevices()
mRecordingDeviceList.emplace_back(name, guid);
}
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ // Flag the device is no longer being interacted with for the Co-routine in case something goes wrong.
+ gWebRTCUpdateDevices = false;
+ // [FIRE-36022]
for (auto &observer : mVoiceDevicesObserverList)
{
observer->OnDevicesChanged(mPlayoutDeviceList, mRecordingDeviceList);
}
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ }
+ // There are two exceptions that unique_lock can trigger, operation_not_permitted or resource_deadlock_would_occur
+ catch (const std::system_error& e)
+ {
+ if (e.code() == std::errc::resource_deadlock_would_occur)
+ {
+ // Another thead may have alreayd called this method
+ mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
+ }
+ else if (e.code() == std::errc::operation_not_permitted)
+ {
+ // This should not be reached
+ mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
+ }
+ else
+ {
+ // Log any other message
+ mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
+ }
+ // Device no longer being interacted with
+ gWebRTCUpdateDevices = false;
+ return;
+ }
+ catch (const std::exception& e)
+ {
+ mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
+ // Device no longer being interacted with
+ gWebRTCUpdateDevices = false;
+ return;
+ }
+ // [FIRE-36022]
}
void LLWebRTCImpl::OnDevicesUpdated()
@@ -726,7 +851,56 @@ void LLWebRTCImpl::intSetMute(bool mute, int delay_ms)
{
if (mDeviceModule)
{
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ try // Try catch needed for uniquie lock as will throw an exception if a second lock is attempted or the mutex is
+ // invalid
+ {
+ // Attempt to lock the access to the audio device, wait up to 1 second for other threads to unlock.
+ std::unique_lock lock(gAudioDeviceMutex, 1s);
+ // If the lock could not be accessed, return as we don't have hardware access and will need to try again another pass.
+ // Prevents threads from interacting with the hardware at the same time as other audio/voice threads.
+ if (!lock.owns_lock())
+ {
+ return;
+ }
+ // Flag the device is being interacted with for the Co-routine in case something goes wrong.
+ gWebRTCUpdateDevices = true;
+ // [FIRE-36022]
mDeviceModule->ForceStopRecording();
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ // Finally signal to the co-routine everyting is OK.
+ gWebRTCUpdateDevices = false;
+ }
+ // There are two exceptions that unique_lock can trigger, operation_not_permitted or resource_deadlock_would_occur
+ catch (const std::system_error& e)
+ {
+ if (e.code() == std::errc::resource_deadlock_would_occur)
+ {
+ // Another thead may have alreayd called this method
+ mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
+ }
+ else if (e.code() == std::errc::operation_not_permitted)
+ {
+ // This should not be reached
+ mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
+ }
+ else
+ {
+ // Log any other message
+ mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
+ }
+ // Device no longer being interacted with
+ gWebRTCUpdateDevices = false;
+ return;
+ }
+ catch (const std::exception& e)
+ {
+ mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
+ // Device no longer being interacted with
+ gWebRTCUpdateDevices = false;
+ return;
+ }
+ // [FIRE-36022]
}
},
webrtc::TimeDelta::Millis(delay_ms));
@@ -738,8 +912,57 @@ void LLWebRTCImpl::intSetMute(bool mute, int delay_ms)
{
if (mDeviceModule)
{
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ try // Try catch needed for uniquie lock as will throw an exception if a second lock is attempted or the mutex is
+ // invalid
+ {
+ // Attempt to lock the access to the audio device, wait up to 1 second for other threads to unlock.
+ std::unique_lock lock(gAudioDeviceMutex, 1s);
+ // If the lock could not be accessed, return as we don't have hardware access and will need to try again another pass.
+ // Prevents threads from interacting with the hardware at the same time as other audio/voice threads.
+ if (!lock.owns_lock())
+ {
+ return;
+ }
+ // Flag the device is being interacted with for the Co-routine in case something goes wrong.
+ gWebRTCUpdateDevices = true;
+ // [FIRE-36022]
mDeviceModule->InitRecording();
mDeviceModule->ForceStartRecording();
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ // Finally signal to the co-routine everyting is OK.
+ gWebRTCUpdateDevices = false;
+ }
+ // There are two exceptions that unique_lock can trigger, operation_not_permitted or resource_deadlock_would_occur
+ catch (const std::system_error& e)
+ {
+ if (e.code() == std::errc::resource_deadlock_would_occur)
+ {
+ // Another thead may have alreayd called this method
+ mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
+ }
+ else if (e.code() == std::errc::operation_not_permitted)
+ {
+ // This should not be reached
+ mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
+ }
+ else
+ {
+ // Log any other message
+ mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
+ }
+ // Device no longer being interacted with
+ gWebRTCUpdateDevices = false;
+ return;
+ }
+ catch (const std::exception& e)
+ {
+ mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what());
+ // Device no longer being interacted with
+ gWebRTCUpdateDevices = false;
+ return;
+ }
+ // [FIRE-36022]
}
});
}
diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h
index 7d06b7d2b4..acd3b686fa 100644
--- a/indra/llwebrtc/llwebrtc.h
+++ b/indra/llwebrtc/llwebrtc.h
@@ -40,6 +40,10 @@
#include
#include
+// [FIRE-36022] - Removing my USB headset crashes entire viewer
+// Needed for inline variable for the crash check
+#include
+// [FIRE-36022]
#ifdef LL_MAKEDLL
#ifdef WEBRTC_WIN
@@ -53,6 +57,12 @@
#define LLSYMEXPORT /**/
#endif // LL_MAKEDLL
+// [FIRE-36022] - Removing my USB headset crashes entire viewer
+// Create an atomic inline flag that will be shared between the various WebRTC threads and co-routines
+// to track of when the audio hardware is being talked to. The co-routine can use it to
+// exit if it too many iterations with the hardware locked indicating that the worker thread died.
+inline std::atomic gWebRTCUpdateDevices = false;
+// [FIRE-36022]
namespace llwebrtc
{
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index fbdb48cf0c..0ffc53c411 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -1016,6 +1016,11 @@ bool LLAppViewerWin32::cleanup()
gDXHardware.cleanup();
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ // Need to unilitialize connection to COM, otherwise it will be treated as a memroy leak.
+ CoUninitialize();
+ // [FIRE-36022]
+
if (mIsConsoleAllocated)
{
FreeConsole();
@@ -1056,6 +1061,19 @@ bool LLAppViewerWin32::initWindow()
LL_WARNS("AppInit") << "Unable to set WindowWidth and WindowHeight for FullScreen mode" << LL_ENDL;
}
}
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ // Acccording to the FMOD spec, you are suppose to initalize COM on the thead that will talk to FMOD. IE the main thread.
+ // There is a coorisponding CoUninitialize in the shutdown code. Otherwise, FMOD will force the initalize with a warning, but does not clean up COM
+ HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
+ if (SUCCEEDED(hr))
+ {
+ LL_INFOS() << "WIN32: CoInitializeEx COM as COINIT_APARTMENTTHREADED Successful" << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS() << "WIN32: CoInitializeEx COM as COINIT_APARTMENTTHREADED Failed" << LL_ENDL;
+ }
+ // [FIRE-36022]
return LLAppViewer::initWindow();
}
diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h
index 33ed6b969d..aea9893686 100644
--- a/indra/newview/llvoiceclient.h
+++ b/indra/newview/llvoiceclient.h
@@ -38,6 +38,10 @@ class LLVOAvatar;
#include "llcallingcard.h" // for LLFriendObserver
#include "llsecapi.h"
#include "llcontrol.h"
+// [FIRE-36022] - Removing my USB headset crashes entire viewer
+// Need to include for audio device mutex shared with other audio/voice systems.
+#include "inlinemutexs.h"
+// [FIRE-36022]
// devices
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
index 0f57e50228..e45ab3d3bd 100644
--- a/indra/newview/llvoicevivox.cpp
+++ b/indra/newview/llvoicevivox.cpp
@@ -82,6 +82,9 @@ extern void handle_voice_morphing_subscribe();
const std::string VIVOX_VOICE_SERVER_TYPE = "vivox";
+// [FIRE-36022] - Removing my USB headset crashes entire viewer
+using namespace std::chrono_literals; // Needed for shared timed mutex to use time
+// [FIRE-36022]
namespace {
const F32 VOLUME_SCALE_VIVOX = 0.01f;
@@ -2413,6 +2416,19 @@ void LLVivoxVoiceClient::sendCaptureAndRenderDevices()
{
if (mCaptureDeviceDirty || mRenderDeviceDirty)
{
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ try // Try catch needed for uniquie lock as will throw an exception if a second lock is attempted or the mutex is invalid
+ {
+ // Attempt to lock the access to the audio device, wait up to 1 second for other threads to unlock.
+ std::unique_lock lock(gAudioDeviceMutex, 1s);
+ // If the lock could not be accessed, return as we don't have hardware access and will need to try again another pass.
+ // Prevents threads from interacting with the hardware at the same time as other audio/voice threads.
+ if (!lock.owns_lock())
+ {
+ LL_INFOS() << "Could not access the audio device mutex, trying again later" << LL_ENDL;
+ return;
+ }
+ // [FIRE-36022]
std::ostringstream stream;
buildSetCaptureDevice(stream);
@@ -2424,6 +2440,35 @@ void LLVivoxVoiceClient::sendCaptureAndRenderDevices()
}
llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ }
+ // There are two exceptions that unique_lock can trigger, operation_not_permitted or resource_deadlock_would_occur
+ catch (const std::system_error& e)
+ {
+ if (e.code() == std::errc::resource_deadlock_would_occur)
+ {
+ // When trying to lock the same lock a second time
+ LL_WARNS() << "Exception Vinvox: " << e.code() << " " << e.what() << LL_ENDL;
+ }
+ else if (e.code() == std::errc::operation_not_permitted)
+ {
+ // When the mutex is invalid
+ LL_WARNS() << "Exception Vinvox: " << e.code() << " " << e.what() << LL_ENDL;
+ }
+ else
+ {
+ // Everything else
+ LL_WARNS() << "Exception Vinvox: " << e.code() << " " << e.what() << LL_ENDL;
+ }
+
+ return;
+ }
+ catch (const std::exception& e)
+ {
+ LL_WARNS() << "Exception Vinvox: " << " " << e.what() << LL_ENDL;
+ return;
+ }
+ // [FIRE-36022]
}
}
diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp
index acda5a5a44..1633f7a957 100644
--- a/indra/newview/llvoicewebrtc.cpp
+++ b/indra/newview/llvoicewebrtc.cpp
@@ -80,6 +80,9 @@
const std::string WEBRTC_VOICE_SERVER_TYPE = "webrtc";
+// [FIRE-36022] - Removing my USB headset crashes entire viewer
+using namespace std::chrono_literals; // Needed for shared timed mutex to use time
+// [FIRE-36022]
namespace {
const F32 MAX_AUDIO_DIST = 50.0f;
@@ -506,6 +509,12 @@ void LLWebRTCVoiceClient::voiceConnectionCoro()
try
{
LLMuteList::getInstance()->addObserver(this);
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ // Add a counter to check if the main thread locked up
+ // to prevent this thread/corutine form filling up
+ // the mMainQueue.
+ static U32 crash_check = 0;
+ // [FIRE-36022]
while (!sShuttingDown)
{
LL_PROFILE_ZONE_NAMED_CATEGORY_VOICE("voiceConnectionCoroLoop")
@@ -591,6 +600,26 @@ void LLWebRTCVoiceClient::voiceConnectionCoro()
// to send position updates.
updatePosition();
}
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ // If the device locked, count up by 1
+ if (gWebRTCUpdateDevices)
+ {
+ crash_check++;
+ }
+ // Else if the device is not locked, then reset the counter back to 0
+ else
+ {
+ crash_check = 0;
+ }
+ // If there are over 10 cycles of the devices being locked, there is a good
+ // chance that the thread failed due to hardware/audio engine issue.
+ if (crash_check > 10)
+ {
+ LL_WARNS() << "WebRTC detected locked worker thread, will shutdown to prevent total viewer lockup." << LL_ENDL;
+ // Exit out of the thread and flag WebRTC to shutdown, hopefully clearing the lock and allowing the viewer to continue.
+ sShuttingDown = true;
+ }
+ // [FIRE-36022]
}
LL::WorkQueue::postMaybe(mMainQueue,
[=, this] {
@@ -731,6 +760,19 @@ void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceLi
void LLWebRTCVoiceClient::OnDevicesChangedImpl(const llwebrtc::LLWebRTCVoiceDeviceList &render_devices,
const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices)
{
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ try // Try catch needed for uniquie lock as will throw an exception if a second lock is attempted or the mutex is invalid
+ {
+ // Attempt to lock the access to the audio device, wait up to 1 second for other threads to unlock.
+ std::unique_lock lock(gAudioDeviceMutex, 1s);
+ // If the lock could not be accessed, return as we don't have hardware access and will need to try again another pass.
+ // Prevents threads from interacting with the hardware at the same time as other audio/voice threads.
+ if (!lock.owns_lock())
+ {
+ LL_INFOS() << "Could not access the audio device mutex, trying again later" << LL_ENDL;
+ return;
+ }
+ // [FIRE-36022]
if (sShuttingDown)
{
return;
@@ -774,6 +816,34 @@ void LLWebRTCVoiceClient::OnDevicesChangedImpl(const llwebrtc::LLWebRTCVoiceDevi
}
setDevicesListUpdated(true);
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ }
+ catch (const std::system_error& e)
+ {
+ if (e.code() == std::errc::resource_deadlock_would_occur)
+ {
+ // When trying to lock the same lock a second time
+ LL_WARNS() << "Exception WebRTC: " << e.code() << " " << e.what() << LL_ENDL;
+ }
+ else if (e.code() == std::errc::operation_not_permitted)
+ {
+ // When the mutex is invalid
+ LL_WARNS() << "Exception WebRTC: " << e.code() << " " << e.what() << LL_ENDL;
+ }
+ else
+ {
+ // Everything else
+ LL_WARNS() << "Exception WebRTC: " << e.code() << " " << e.what() << LL_ENDL;
+ }
+
+ return;
+ }
+ catch (const std::exception& e)
+ {
+ LL_WARNS() << "Exception WebRTC: " << " " << e.what() << LL_ENDL;
+ return;
+ }
+ // [FIRE-36022]
}
void LLWebRTCVoiceClient::clearRenderDevices()
@@ -848,6 +918,11 @@ void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume)
float LLWebRTCVoiceClient::tuningGetEnergy(void)
{
+ // [FIRE-36022] - Removing my USB headset crashes entire viewer
+ // This can cause an error if device interface can be NULL.
+ if (!mWebRTCDeviceInterface)
+ return 1.0f;
+ // [FIRE-36022]
float rms = mWebRTCDeviceInterface->getTuningAudioLevel();
return TUNING_LEVEL_START_POINT - TUNING_LEVEL_SCALE * rms;
}