From 0cdae450d884ecd18fc0f596b58a194ed6cb951a Mon Sep 17 00:00:00 2001 From: minerjr Date: Tue, 28 Oct 2025 23:05:53 -0300 Subject: [PATCH 01/42] FIRE-36022- Removing my USB headset crashes entire viewer - WebRTC fix This is created to address the issue of USB headsets being removed or Bluetooth headsets with bad connections causing the viewer to lock up. First Part: WebRTC WebRTC now has an added inline atomic bool used to track when the worker thread is accessing the audio hardware. If the coroutine that is always running detects 10 consecutive true flag values, it will set WebRTC to exit and exit itself. This coroutine was what was locking up the viewer as it spammed the mMainQueue with messages every update, but the worker thread was locked up and unable to receive the messages. Once the queue reached 1024 messages, it would lock up. Underlying issue is the Windows Core Audio driver system crashes if too many threads have requests to get audio updates upon hardware changes. And with a USB headset having 2 devices in 1, it multiples the updates by 2x. The WebRTC audio system itself locks a mutex as it calls the core audio system to get set the input, but if it is already being processed by another thread, it then gets an exception to try again. The audio driver is not handling the message and crashes and does not return leaving the WebRTC worker thread locked. --- indra/llwebrtc/llwebrtc.cpp | 35 +++++++++++++++++++++++++++++++++ indra/llwebrtc/llwebrtc.h | 10 ++++++++++ indra/newview/llvoicewebrtc.cpp | 31 +++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+) diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index d686082878..eea5c92b76 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -459,6 +459,10 @@ void LLWebRTCImpl::workerDeployDevices() { 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 + iWebRTCUpdateDevices = false; + // [FIRE-36022] - Removing my USB headset crashes entire viewer return; } @@ -483,6 +487,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. + iWebRTCUpdateDevices = true; + // [FIRE-36022] mDeviceModule->StopPlayout(); mDeviceModule->ForceStopRecording(); #if WEBRTC_WIN @@ -546,6 +554,10 @@ void LLWebRTCImpl::workerDeployDevices() { mDeviceModule->StartPlayout(); } + // [FIRE-36022] - Removing my USB headset crashes entire viewer + // Finally signal to the co-routine everyting is OK. + iWebRTCUpdateDevices = false; + // [FIRE-36022] mSignalingThread->PostTask( [this] { @@ -589,6 +601,10 @@ void LLWebRTCImpl::updateDevices() 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. + iWebRTCUpdateDevices = true; + // [FIRE-36022] int16_t renderDeviceCount = mDeviceModule->PlayoutDevices(); mPlayoutDeviceList.clear(); @@ -625,6 +641,10 @@ 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. + iWebRTCUpdateDevices = false; + // [FIRE-36022] for (auto &observer : mVoiceDevicesObserverList) { observer->OnDevicesChanged(mPlayoutDeviceList, mRecordingDeviceList); @@ -726,7 +746,14 @@ void LLWebRTCImpl::intSetMute(bool mute, int delay_ms) { if (mDeviceModule) { + // Flag the device is being interacted with for the Co-routine in case something goes wrong. + iWebRTCUpdateDevices = true; + // [FIRE-36022] mDeviceModule->ForceStopRecording(); + // [FIRE-36022] - Removing my USB headset crashes entire viewer + // Finally signal to the co-routine everyting is OK. + iWebRTCUpdateDevices = false; + // [FIRE-36022] } }, webrtc::TimeDelta::Millis(delay_ms)); @@ -738,8 +765,16 @@ void LLWebRTCImpl::intSetMute(bool mute, int delay_ms) { if (mDeviceModule) { + // [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. + iWebRTCUpdateDevices = 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. + iWebRTCUpdateDevices = false; + // [FIRE-36022] } }); } diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index 7d06b7d2b4..572f5ecce9 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 iWebRTCUpdateDevices = false; +// [FIRE-36022] namespace llwebrtc { diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 5ff1b0c498..a00e736d05 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -506,6 +506,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 +597,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 (iWebRTCUpdateDevices) + { + 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] { @@ -848,6 +874,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; } From 6ad9a57c3943543a55c53539d36c8c951208ba8b Mon Sep 17 00:00:00 2001 From: minerjr Date: Tue, 28 Oct 2025 23:22:13 -0300 Subject: [PATCH 02/42] FIRE-36022 - Removing my USB headset crashes entire viewer - audio device mutex As with the previous change, this is to address the issue FIRE-36022 where users who remove their USB headsets with microphones are having the viewer lock up. This introduces a new inline mutex called iAudioDeviceMutex. This is stored in a shared header file located on llcommon. iAudioDeviceMutex is a timed_mutex which allows for a time limit to be placed on a wait for a lock. Once the time runs out or the lock is achiveved the code moves on. With a check after the lock, if the owner is the current thread, then the lock was successful, otherwise it's still being used by another thread and the function should exit to try again later on. This logic is wrapping WebRTC, FMOD and Vinvox calls to the audio hardware by the Viewer. OpenAL does not currently support changing of audio hardware and always defaults to the default audio hardware. Added exceptions handling for the new unique_lock with the timed_mutex as they can throw 2 exceptions if the thread is already locked by the current thread and if the mutex is invalid. Further testing may reveal other areas which would need the timed_mutex added to protect from threads locking up. --- indra/llaudio/llaudioengine.h | 4 + indra/llaudio/llaudioengine_fmodstudio.cpp | 86 ++++++++++ indra/llcommon/CMakeLists.txt | 1 + indra/llcommon/inlinemutexs.h | 39 +++++ indra/llwebrtc/llwebrtc.cpp | 188 +++++++++++++++++++++ indra/newview/llvoiceclient.h | 4 + indra/newview/llvoicevivox.cpp | 45 +++++ indra/newview/llvoicewebrtc.cpp | 44 +++++ 8 files changed, 411 insertions(+) create mode 100644 indra/llcommon/inlinemutexs.h 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..03964762b2 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); @@ -114,7 +117,11 @@ 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 { + 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 +131,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 3 seconds for other threads to unlock. + std::unique_lock lock(iAudioDeviceMutex, 3s); + // 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 (not 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 +150,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) @@ -373,7 +419,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 3 seconds for other threads to unlock. + std::unique_lock lock(iAudioDeviceMutex, 3s); + // 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 (not 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 4f9371002e..0e54d25031 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..38030f88e7 --- /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 iAudioDeviceMutex; +#endif diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index eea5c92b76..2ed9cfc071 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 iAudioDeviceMutex; +// 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,6 +474,18 @@ 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 3 seconds for other threads to unlock. + std::unique_lock lock(iAudioDeviceMutex, 3s); + // 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 (not lock.owns_lock()) + { + return; + } + // [FIRE-36022] if (!mDeviceModule) { // [FIRE-36022] @@ -578,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 + iWebRTCUpdateDevices = false; + return; + } + catch (const std::exception& e) + { + mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what()); + // Device no longer being interacted with + iWebRTCUpdateDevices = false; + return; + } + // [FIRE-36022] } void LLWebRTCImpl::setCaptureDevice(const std::string &id) @@ -596,6 +657,18 @@ 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 3 seconds for other threads to unlock. + std::unique_lock lock(iAudioDeviceMutex, 3s); + // 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 (not lock.owns_lock()) + { + return; + } + // [FIRE-36022] if (!mDeviceModule) { return; @@ -649,6 +722,38 @@ void LLWebRTCImpl::updateDevices() { 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 + iWebRTCUpdateDevices = false; + return; + } + catch (const std::exception& e) + { + mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what()); + // Device no longer being interacted with + iWebRTCUpdateDevices = false; + return; + } + // [FIRE-36022] } void LLWebRTCImpl::OnDevicesUpdated() @@ -746,6 +851,18 @@ 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 3 seconds for other threads to unlock. + std::unique_lock lock(iAudioDeviceMutex, 3s); + // 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 (not lock.owns_lock()) + { + return; + } // Flag the device is being interacted with for the Co-routine in case something goes wrong. iWebRTCUpdateDevices = true; // [FIRE-36022] @@ -753,6 +870,36 @@ void LLWebRTCImpl::intSetMute(bool mute, int delay_ms) // [FIRE-36022] - Removing my USB headset crashes entire viewer // Finally signal to the co-routine everyting is OK. iWebRTCUpdateDevices = 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 + iWebRTCUpdateDevices = false; + return; + } + catch (const std::exception& e) + { + mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what()); + // Device no longer being interacted with + iWebRTCUpdateDevices = false; + return; + } // [FIRE-36022] } }, @@ -766,6 +913,17 @@ 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 3 seconds for other threads to unlock. + std::unique_lock lock(iAudioDeviceMutex, 3s); + // 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 (not lock.owns_lock()) + { + return; + } // Flag the device is being interacted with for the Co-routine in case something goes wrong. iWebRTCUpdateDevices = true; // [FIRE-36022] @@ -774,6 +932,36 @@ void LLWebRTCImpl::intSetMute(bool mute, int delay_ms) // [FIRE-36022] - Removing my USB headset crashes entire viewer // Finally signal to the co-routine everyting is OK. iWebRTCUpdateDevices = 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 + iWebRTCUpdateDevices = false; + return; + } + catch (const std::exception& e) + { + mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what()); + // Device no longer being interacted with + iWebRTCUpdateDevices = false; + return; + } // [FIRE-36022] } }); 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..6ae5441270 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 3 seconds for other threads to unlock. + std::unique_lock lock(iAudioDeviceMutex, 3s); + // 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 (not 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 a00e736d05..046fa40a91 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; @@ -757,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 3 seconds for other threads to unlock. + std::unique_lock lock(iAudioDeviceMutex, 3s); + // 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 (not lock.owns_lock()) + { + LL_INFOS() << "Could not access the audio device mutex, trying again later" << LL_ENDL; + return; + } + // [FIRE-36022] if (sShuttingDown) { return; @@ -800,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() From cc3bae406c2bf788f7b0c0237699b708714dde06 Mon Sep 17 00:00:00 2001 From: minerjr Date: Tue, 28 Oct 2025 23:30:18 -0300 Subject: [PATCH 03/42] FIRE-36022 - Removing my USB headset crashes entire viewer - FMOD fixes Added three FMOD specific changes based upon reading the FMOD manual. See: https://www.fmod.com/docs/2.00/api/platforms-win.html As well, switched to using the FMOD log version of the library and found one error on the get advanced features. Also, the call back method should be a static method. Just being cautious. --- indra/llaudio/llaudioengine_fmodstudio.cpp | 9 ++++++++- indra/newview/llappviewerwin32.cpp | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp index 03964762b2..36b35efb47 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.cpp +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -116,8 +116,10 @@ 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 { @@ -357,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; diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index ba3e900580..bf12160aa0 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(); } From f6ce46c726eebb2c374cb023c10745735c4330ef Mon Sep 17 00:00:00 2001 From: minerjr Date: Wed, 29 Oct 2025 15:21:51 -0300 Subject: [PATCH 04/42] Fixed up not python operator to proper C++ operator Fixed up the follow Firestorm coding standards. --- indra/llaudio/llaudioengine_fmodstudio.cpp | 4 ++-- indra/llwebrtc/llwebrtc.cpp | 8 ++++---- indra/newview/llvoicevivox.cpp | 2 +- indra/newview/llvoicewebrtc.cpp | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp index 36b35efb47..fab0bfabda 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.cpp +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -138,7 +138,7 @@ static FMOD_RESULT F_CALL systemCallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBA std::unique_lock lock(iAudioDeviceMutex, 3s); // 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 (not lock.owns_lock()) + 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; @@ -433,7 +433,7 @@ void LLAudioEngine_FMODSTUDIO::setDevice(const LLUUID& device_uuid) std::unique_lock lock(iAudioDeviceMutex, 3s); // 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 (not lock.owns_lock()) + if (!lock.owns_lock()) { LL_INFOS() << "Could not access the audio device mutex, trying again later" << LL_ENDL; return; diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 2ed9cfc071..4aeb152278 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -481,7 +481,7 @@ void LLWebRTCImpl::workerDeployDevices() std::unique_lock lock(iAudioDeviceMutex, 3s); // 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 (not lock.owns_lock()) + if (!lock.owns_lock()) { return; } @@ -664,7 +664,7 @@ void LLWebRTCImpl::updateDevices() std::unique_lock lock(iAudioDeviceMutex, 3s); // 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 (not lock.owns_lock()) + if (!lock.owns_lock()) { return; } @@ -859,7 +859,7 @@ void LLWebRTCImpl::intSetMute(bool mute, int delay_ms) std::unique_lock lock(iAudioDeviceMutex, 3s); // 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 (not lock.owns_lock()) + if (!lock.owns_lock()) { return; } @@ -920,7 +920,7 @@ void LLWebRTCImpl::intSetMute(bool mute, int delay_ms) std::unique_lock lock(iAudioDeviceMutex, 3s); // 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 (not lock.owns_lock()) + if (!lock.owns_lock()) { return; } diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 6ae5441270..11f0c96083 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -2423,7 +2423,7 @@ void LLVivoxVoiceClient::sendCaptureAndRenderDevices() std::unique_lock lock(iAudioDeviceMutex, 3s); // 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 (not lock.owns_lock()) + if (!lock.owns_lock()) { LL_INFOS() << "Could not access the audio device mutex, trying again later" << LL_ENDL; return; diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 046fa40a91..c7d04fd5bf 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -767,7 +767,7 @@ void LLWebRTCVoiceClient::OnDevicesChangedImpl(const llwebrtc::LLWebRTCVoiceDevi std::unique_lock lock(iAudioDeviceMutex, 3s); // 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 (not lock.owns_lock()) + if (!lock.owns_lock()) { LL_INFOS() << "Could not access the audio device mutex, trying again later" << LL_ENDL; return; From b37a210e38bf1ca41614e91a558eba5fd1ec7ca5 Mon Sep 17 00:00:00 2001 From: minerjr Date: Wed, 29 Oct 2025 15:37:55 -0300 Subject: [PATCH 05/42] Fixed up the i prefix to g to follow coding standards Changed iWebRTCUpdateDevices and iAudioDeviceMutex to gWebRTCUpdateDevicesand gAudioDeviceMutex to follow Firestorm coding standards. They are global static variables. --- indra/llaudio/llaudioengine_fmodstudio.cpp | 4 +- indra/llcommon/inlinemutexs.h | 2 +- indra/llwebrtc/llwebrtc.cpp | 44 +++++++++++----------- indra/llwebrtc/llwebrtc.h | 2 +- indra/newview/llvoicevivox.cpp | 2 +- indra/newview/llvoicewebrtc.cpp | 4 +- 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp index fab0bfabda..ddac609b85 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.cpp +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -135,7 +135,7 @@ static FMOD_RESULT F_CALL systemCallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBA { // [FIRE-36022] - Removing my USB headset crashes entire viewer // Attempt to lock the access to the audio device, wait up to 3 seconds for other threads to unlock. - std::unique_lock lock(iAudioDeviceMutex, 3s); + std::unique_lock lock(gAudioDeviceMutex, 3s); // 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()) @@ -430,7 +430,7 @@ void LLAudioEngine_FMODSTUDIO::setDevice(const LLUUID& device_uuid) 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 3 seconds for other threads to unlock. - std::unique_lock lock(iAudioDeviceMutex, 3s); + std::unique_lock lock(gAudioDeviceMutex, 3s); // 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()) diff --git a/indra/llcommon/inlinemutexs.h b/indra/llcommon/inlinemutexs.h index 38030f88e7..3b8ab5c4ef 100644 --- a/indra/llcommon/inlinemutexs.h +++ b/indra/llcommon/inlinemutexs.h @@ -35,5 +35,5 @@ // 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 iAudioDeviceMutex; +inline std::timed_mutex gAudioDeviceMutex; #endif diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 4aeb152278..26e05118da 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -48,7 +48,7 @@ // 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 iAudioDeviceMutex; +inline std::timed_mutex gAudioDeviceMutex; // Need to use to access the 3 second timeout for the lock. using namespace std::chrono_literals; // [FIRE-36022] @@ -478,7 +478,7 @@ void LLWebRTCImpl::workerDeployDevices() 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 3 seconds for other threads to unlock. - std::unique_lock lock(iAudioDeviceMutex, 3s); + std::unique_lock lock(gAudioDeviceMutex, 3s); // 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()) @@ -490,7 +490,7 @@ void LLWebRTCImpl::workerDeployDevices() { // [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 - iWebRTCUpdateDevices = false; + gWebRTCUpdateDevices = false; // [FIRE-36022] - Removing my USB headset crashes entire viewer return; } @@ -518,7 +518,7 @@ 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. - iWebRTCUpdateDevices = true; + gWebRTCUpdateDevices = true; // [FIRE-36022] mDeviceModule->StopPlayout(); mDeviceModule->ForceStopRecording(); @@ -585,7 +585,7 @@ void LLWebRTCImpl::workerDeployDevices() } // [FIRE-36022] - Removing my USB headset crashes entire viewer // Finally signal to the co-routine everyting is OK. - iWebRTCUpdateDevices = false; + gWebRTCUpdateDevices = false; // [FIRE-36022] mSignalingThread->PostTask( [this] @@ -628,14 +628,14 @@ void LLWebRTCImpl::workerDeployDevices() mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what()); } // Device no longer being interacted with - iWebRTCUpdateDevices = false; + gWebRTCUpdateDevices = false; return; } catch (const std::exception& e) { mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what()); // Device no longer being interacted with - iWebRTCUpdateDevices = false; + gWebRTCUpdateDevices = false; return; } // [FIRE-36022] @@ -661,7 +661,7 @@ void LLWebRTCImpl::updateDevices() 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 3 seconds for other threads to unlock. - std::unique_lock lock(iAudioDeviceMutex, 3s); + std::unique_lock lock(gAudioDeviceMutex, 3s); // 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()) @@ -676,7 +676,7 @@ void LLWebRTCImpl::updateDevices() // [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. - iWebRTCUpdateDevices = true; + gWebRTCUpdateDevices = true; // [FIRE-36022] int16_t renderDeviceCount = mDeviceModule->PlayoutDevices(); @@ -716,7 +716,7 @@ void LLWebRTCImpl::updateDevices() // [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. - iWebRTCUpdateDevices = false; + gWebRTCUpdateDevices = false; // [FIRE-36022] for (auto &observer : mVoiceDevicesObserverList) { @@ -743,14 +743,14 @@ void LLWebRTCImpl::updateDevices() mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what()); } // Device no longer being interacted with - iWebRTCUpdateDevices = false; + gWebRTCUpdateDevices = false; return; } catch (const std::exception& e) { mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what()); // Device no longer being interacted with - iWebRTCUpdateDevices = false; + gWebRTCUpdateDevices = false; return; } // [FIRE-36022] @@ -856,7 +856,7 @@ void LLWebRTCImpl::intSetMute(bool mute, int delay_ms) // invalid { // Attempt to lock the access to the audio device, wait up to 3 seconds for other threads to unlock. - std::unique_lock lock(iAudioDeviceMutex, 3s); + std::unique_lock lock(gAudioDeviceMutex, 3s); // 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()) @@ -864,12 +864,12 @@ void LLWebRTCImpl::intSetMute(bool mute, int delay_ms) return; } // Flag the device is being interacted with for the Co-routine in case something goes wrong. - iWebRTCUpdateDevices = true; + gWebRTCUpdateDevices = true; // [FIRE-36022] mDeviceModule->ForceStopRecording(); // [FIRE-36022] - Removing my USB headset crashes entire viewer // Finally signal to the co-routine everyting is OK. - iWebRTCUpdateDevices = false; + 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) @@ -890,14 +890,14 @@ void LLWebRTCImpl::intSetMute(bool mute, int delay_ms) mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what()); } // Device no longer being interacted with - iWebRTCUpdateDevices = false; + gWebRTCUpdateDevices = false; return; } catch (const std::exception& e) { mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what()); // Device no longer being interacted with - iWebRTCUpdateDevices = false; + gWebRTCUpdateDevices = false; return; } // [FIRE-36022] @@ -917,7 +917,7 @@ void LLWebRTCImpl::intSetMute(bool mute, int delay_ms) // invalid { // Attempt to lock the access to the audio device, wait up to 3 seconds for other threads to unlock. - std::unique_lock lock(iAudioDeviceMutex, 3s); + std::unique_lock lock(gAudioDeviceMutex, 3s); // 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()) @@ -925,13 +925,13 @@ void LLWebRTCImpl::intSetMute(bool mute, int delay_ms) return; } // Flag the device is being interacted with for the Co-routine in case something goes wrong. - iWebRTCUpdateDevices = true; + 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. - iWebRTCUpdateDevices = false; + 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) @@ -952,14 +952,14 @@ void LLWebRTCImpl::intSetMute(bool mute, int delay_ms) mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what()); } // Device no longer being interacted with - iWebRTCUpdateDevices = false; + gWebRTCUpdateDevices = false; return; } catch (const std::exception& e) { mLogSink->OnLogMessage(std::string("Excepton: WebRTC: ") + e.what()); // Device no longer being interacted with - iWebRTCUpdateDevices = false; + gWebRTCUpdateDevices = false; return; } // [FIRE-36022] diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index 572f5ecce9..acd3b686fa 100644 --- a/indra/llwebrtc/llwebrtc.h +++ b/indra/llwebrtc/llwebrtc.h @@ -61,7 +61,7 @@ // 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 iWebRTCUpdateDevices = false; +inline std::atomic gWebRTCUpdateDevices = false; // [FIRE-36022] namespace llwebrtc { diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 11f0c96083..3c8b091a48 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -2420,7 +2420,7 @@ void LLVivoxVoiceClient::sendCaptureAndRenderDevices() 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 3 seconds for other threads to unlock. - std::unique_lock lock(iAudioDeviceMutex, 3s); + std::unique_lock lock(gAudioDeviceMutex, 3s); // 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()) diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index c7d04fd5bf..2d13a62904 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -602,7 +602,7 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() } // [FIRE-36022] - Removing my USB headset crashes entire viewer // If the device locked, count up by 1 - if (iWebRTCUpdateDevices) + if (gWebRTCUpdateDevices) { crash_check++; } @@ -764,7 +764,7 @@ void LLWebRTCVoiceClient::OnDevicesChangedImpl(const llwebrtc::LLWebRTCVoiceDevi 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 3 seconds for other threads to unlock. - std::unique_lock lock(iAudioDeviceMutex, 3s); + std::unique_lock lock(gAudioDeviceMutex, 3s); // 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()) From 9ee2595ac196ec08264c9f271e8a7ac757d21cea Mon Sep 17 00:00:00 2001 From: minerjr Date: Wed, 29 Oct 2025 21:39:57 -0300 Subject: [PATCH 06/42] Reduced the time_mutx lock to 1 second from 3 seconds Originally setup with 3 second as a was not sure how long it would take to recover, but 1 second should be good to recover and enumerate devices. --- indra/llaudio/llaudioengine_fmodstudio.cpp | 8 ++++---- indra/llwebrtc/llwebrtc.cpp | 16 ++++++++-------- indra/newview/llvoicevivox.cpp | 4 ++-- indra/newview/llvoicewebrtc.cpp | 4 ++-- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp index ddac609b85..7daaea1c76 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.cpp +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -134,8 +134,8 @@ static FMOD_RESULT F_CALL systemCallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBA 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 3 seconds for other threads to unlock. - std::unique_lock lock(gAudioDeviceMutex, 3s); + // 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()) @@ -429,8 +429,8 @@ void LLAudioEngine_FMODSTUDIO::setDevice(const LLUUID& 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 3 seconds for other threads to unlock. - std::unique_lock lock(gAudioDeviceMutex, 3s); + // 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()) diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 26e05118da..860f619885 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -477,8 +477,8 @@ 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 3 seconds for other threads to unlock. - std::unique_lock lock(gAudioDeviceMutex, 3s); + // 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()) @@ -660,8 +660,8 @@ 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 3 seconds for other threads to unlock. - std::unique_lock lock(gAudioDeviceMutex, 3s); + // 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()) @@ -855,8 +855,8 @@ void LLWebRTCImpl::intSetMute(bool mute, int delay_ms) 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 3 seconds for other threads to unlock. - std::unique_lock lock(gAudioDeviceMutex, 3s); + // 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()) @@ -916,8 +916,8 @@ void LLWebRTCImpl::intSetMute(bool mute, int delay_ms) 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 3 seconds for other threads to unlock. - std::unique_lock lock(gAudioDeviceMutex, 3s); + // 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()) diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 3c8b091a48..e45ab3d3bd 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -2419,8 +2419,8 @@ void LLVivoxVoiceClient::sendCaptureAndRenderDevices() // [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 3 seconds for other threads to unlock. - std::unique_lock lock(gAudioDeviceMutex, 3s); + // 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()) diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 2d13a62904..cfc9456582 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -763,8 +763,8 @@ void LLWebRTCVoiceClient::OnDevicesChangedImpl(const llwebrtc::LLWebRTCVoiceDevi // [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 3 seconds for other threads to unlock. - std::unique_lock lock(gAudioDeviceMutex, 3s); + // 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()) From c4915f9932283a4aecad03975a6a21d830f6d777 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Nov 2025 10:05:14 +0000 Subject: [PATCH 07/42] Bump actions/download-artifact from 5 to 6 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 5 to 6. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yaml | 10 +++++----- .github/workflows/build_viewer.yml | 2 +- .github/workflows/sign.yml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index c6be304aa0..608bb0b880 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -368,13 +368,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Download viewer exe - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 with: name: Windows-app path: _artifacts - name: Download Windows Symbols if: env.BUGSPLAT_USER && env.BUGSPLAT_PASS - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 with: name: Windows-symbols - name: Extract viewer pdb @@ -404,7 +404,7 @@ jobs: steps: - name: Download Mac Symbols if: env.BUGSPLAT_USER && env.BUGSPLAT_PASS - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 with: name: macOS-symbols - name: Post Mac symbols @@ -424,11 +424,11 @@ jobs: runs-on: ubuntu-latest if: needs.setup.outputs.release_run steps: - - uses: actions/download-artifact@v5 + - uses: actions/download-artifact@v6 with: pattern: "*-installer" - - uses: actions/download-artifact@v5 + - uses: actions/download-artifact@v6 with: pattern: "*-metadata" diff --git a/.github/workflows/build_viewer.yml b/.github/workflows/build_viewer.yml index 4d74d5ba3d..19ced55f63 100644 --- a/.github/workflows/build_viewer.yml +++ b/.github/workflows/build_viewer.yml @@ -529,7 +529,7 @@ jobs: run: pip install discord-webhook - name: Download artifacts - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 id: download with: path: to_deploy diff --git a/.github/workflows/sign.yml b/.github/workflows/sign.yml index 828f8fd6ad..17ec349913 100644 --- a/.github/workflows/sign.yml +++ b/.github/workflows/sign.yml @@ -86,7 +86,7 @@ jobs: shell: bash - name: Download Setup.exe Files Artifact - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 with: name: setup-exe-files path: setup_exe_files From 4699e2f5d24403e8b72137f464bc6d316537b098 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Nov 2025 10:05:18 +0000 Subject: [PATCH 08/42] Bump azure/trusted-signing-action from 0.5.9 to 0.5.10 Bumps [azure/trusted-signing-action](https://github.com/azure/trusted-signing-action) from 0.5.9 to 0.5.10. - [Release notes](https://github.com/azure/trusted-signing-action/releases) - [Commits](https://github.com/azure/trusted-signing-action/compare/v0.5.9...v0.5.10) --- updated-dependencies: - dependency-name: azure/trusted-signing-action dependency-version: 0.5.10 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/sign.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sign.yml b/.github/workflows/sign.yml index 828f8fd6ad..edb1ca3e61 100644 --- a/.github/workflows/sign.yml +++ b/.github/workflows/sign.yml @@ -115,7 +115,7 @@ jobs: # wait-for-completion: true # output-artifact-directory: 'application-signed' - name: Azure Trusted Signing - uses: azure/trusted-signing-action@v0.5.9 + uses: azure/trusted-signing-action@v0.5.10 with: azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} From b0f179945ea0815564914b85f9f467cdc42b4c30 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Nov 2025 10:05:23 +0000 Subject: [PATCH 09/42] Bump actions/upload-artifact from 4 to 5 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yaml | 8 ++++---- .github/workflows/build_viewer.yml | 8 ++++---- .github/workflows/sign.yml | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index c6be304aa0..f8cda60a35 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -264,7 +264,7 @@ jobs: - name: Upload executable if: steps.build.outputs.viewer_app - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: "${{ steps.build.outputs.artifact }}-app" path: | @@ -274,13 +274,13 @@ jobs: # artifact for that too. - name: Upload symbol file if: steps.build.outputs.symbolfile - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: "${{ steps.build.outputs.artifact }}-symbols" path: ${{ steps.build.outputs.symbolfile }} - name: Upload metadata - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: "${{ steps.build.outputs.artifact }}-metadata" # emitted by build.sh, possibly multiple lines @@ -288,7 +288,7 @@ jobs: ${{ steps.build.outputs.metadata }} - name: Upload physics package - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 # should only be set for viewer-private if: matrix.configuration == 'Release' && steps.build.outputs.physicstpv with: diff --git a/.github/workflows/build_viewer.yml b/.github/workflows/build_viewer.yml index 4d74d5ba3d..0def675c62 100644 --- a/.github/workflows/build_viewer.yml +++ b/.github/workflows/build_viewer.yml @@ -482,7 +482,7 @@ jobs: dumpSyms: false - name: Publish artifacts if: runner.os == 'Windows' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: ${{ env.FS_RELEASE_TYPE }}-${{ matrix.os }}-${{ matrix.variant }}-${{ matrix.grid }}-artifacts.zip path: | @@ -491,7 +491,7 @@ jobs: - name: publish Linux artifacts if: runner.os == 'Linux' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: ${{ env.FS_RELEASE_TYPE }}-${{ matrix.os }}-${{ matrix.variant }}-${{matrix.grid}}-artifacts.zip path: | @@ -500,7 +500,7 @@ jobs: - name: publish MacOS artifacts if: runner.os == 'macOS' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: ${{ env.FS_RELEASE_TYPE }}-${{ matrix.os }}-${{ matrix.variant }}-${{matrix.grid}}-artifacts.zip path: | @@ -581,7 +581,7 @@ jobs: # Upload Build Info Artifact (note that this file is expected to be identical for each matrix run, so items like OS and VARIANT cannot be in this file.) - name: Upload Tag Info - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: build_info path: build_info.json diff --git a/.github/workflows/sign.yml b/.github/workflows/sign.yml index 828f8fd6ad..2b2780b4b0 100644 --- a/.github/workflows/sign.yml +++ b/.github/workflows/sign.yml @@ -62,7 +62,7 @@ jobs: files_json=$(ls setup_exe_files | jq -R . | jq -s -c .) echo "setup_files=$files_json" >> $GITHUB_OUTPUT - name: Upload Setup.exe Files - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: setup-exe-files path: setup_exe_files/ @@ -97,7 +97,7 @@ jobs: shell: bash # - name: Upload unsigned artifact # id: upload-unsigned-artifact - # uses: actions/upload-artifact@v4 + # uses: actions/upload-artifact@v5 # with: # name: unsigned-artifact-${{ matrix.file }} # path: to_sign/${{ matrix.file }} @@ -129,14 +129,14 @@ jobs: files-folder-filter: exe # - name: Upload signed artifact - # uses: actions/upload-artifact@v4 + # uses: actions/upload-artifact@v5 # with: # name: signed-artifact-${{ matrix.file }} # path: ./application-signed # - name: Upload unsigned artifact - name: Upload signed artifact id: upload-unsigned-artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: unsigned-artifact-${{ matrix.file }} path: to_sign\${{ matrix.file }} From 834b198a87563f3725005790e76e8492ed7e74dc Mon Sep 17 00:00:00 2001 From: Hecklezz Date: Sun, 2 Nov 2025 01:10:19 +1000 Subject: [PATCH 10/42] Fix issue where mac was crashing during an attempt to unplug or replug microphone (FS:TJ- Cherry-pick to help fix FIRE-36022 on macOS) The mac audio device manager was being "helpful" by restarting playout and recording if the Default device was changed, assuming the application wouldn't care. However, we received an update of device change, and attempted to stop and start playout anyway, causing a conflict. The fix was simply to not deploy new devices when the device id didn't change. --- indra/llwebrtc/llwebrtc.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 860f619885..198161d0c2 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -644,14 +644,20 @@ void LLWebRTCImpl::workerDeployDevices() void LLWebRTCImpl::setCaptureDevice(const std::string &id) { - mRecordingDevice = id; - deployDevices(); + if (mRecordingDevice != id) + { + mRecordingDevice = id; + deployDevices(); + } } void LLWebRTCImpl::setRenderDevice(const std::string &id) { - mPlayoutDevice = id; - deployDevices(); + if (mPlayoutDevice != id) + { + mPlayoutDevice = id; + deployDevices(); + } } // updateDevices needs to happen on the worker thread. From 5f9daef8e00f3f2ae8a36004f3194a2613a17e05 Mon Sep 17 00:00:00 2001 From: minerjr Date: Sun, 2 Nov 2025 14:54:55 -0400 Subject: [PATCH 11/42] Updated to match upstream fix LL ended up fixing the same issue upstream, but returned 0.0f instead of 1.0f. Updated code to match formatting LL used and value. --- indra/newview/llvoicewebrtc.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index cfc9456582..6b2e9890ae 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -921,7 +921,9 @@ 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; + { + return 0.0f; + } // [FIRE-36022] float rms = mWebRTCDeviceInterface->getTuningAudioLevel(); return TUNING_LEVEL_START_POINT - TUNING_LEVEL_SCALE * rms; From 76c9cb0a2cd7fb63e991395a8b621eb3c378f685 Mon Sep 17 00:00:00 2001 From: Hecklezz Date: Mon, 3 Nov 2025 06:10:03 +1000 Subject: [PATCH 12/42] [FIRE-35530] Fix CFBundleIndentifier syntax being incorrect causing it to be missing --- indra/newview/Info-Firestorm.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/Info-Firestorm.plist b/indra/newview/Info-Firestorm.plist index 6b2e73a5a2..add3bef56b 100644 --- a/indra/newview/Info-Firestorm.plist +++ b/indra/newview/Info-Firestorm.plist @@ -11,7 +11,7 @@ CFBundleIconFile ${MACOSX_BUNDLE_ICON_FILE} CFBundleIdentifier - ${PRODUCT_BUNDLE_IDENTIFIER} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString From 334283bc678c236c555d9b1876b5cd07101d3b94 Mon Sep 17 00:00:00 2001 From: PanteraPolnocy Date: Wed, 5 Nov 2025 00:03:39 +0100 Subject: [PATCH 13/42] FIRE-36065 French translation update, by Laurent Bechir --- .../xui/fr/floater_inventory_settings.xml | 14 +++++++++++++- .../fr/floater_snapshot_guide_settings.xml | 19 +++++++++++++++++++ .../default/xui/fr/menu_gallery_inventory.xml | 2 ++ .../xui/fr/menu_gallery_outfit_tab.xml | 2 ++ .../skins/default/xui/fr/menu_inventory.xml | 2 ++ .../xui/fr/menu_outfit_gallery_sort.xml | 6 ++++++ .../skins/default/xui/fr/menu_outfit_gear.xml | 9 +++++---- .../default/xui/fr/menu_outfit_list_sort.xml | 7 +++++++ .../skins/default/xui/fr/menu_outfit_tab.xml | 2 ++ .../xui/fr/menu_wearable_list_item.xml | 2 ++ .../skins/default/xui/fr/menu_wearing_tab.xml | 2 ++ .../default/xui/fr/panel_preferences_chat.xml | 4 +++- 12 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 indra/newview/skins/default/xui/fr/floater_snapshot_guide_settings.xml create mode 100644 indra/newview/skins/default/xui/fr/menu_outfit_gallery_sort.xml create mode 100644 indra/newview/skins/default/xui/fr/menu_outfit_list_sort.xml diff --git a/indra/newview/skins/default/xui/fr/floater_inventory_settings.xml b/indra/newview/skins/default/xui/fr/floater_inventory_settings.xml index 4ae4c3db45..dafc61543a 100644 --- a/indra/newview/skins/default/xui/fr/floater_inventory_settings.xml +++ b/indra/newview/skins/default/xui/fr/floater_inventory_settings.xml @@ -22,5 +22,17 @@ - + + - - - - Script Editor Font: - - - - - - - - - - - - - - - - - - - - - - Preprocessor include path: - - - - - External Editor: - - - @@ -1608,14 +1516,6 @@ tool_tip="When checked, object for sale info is saved on change instead of requiring a confirm." name="FSCommitForSaleOnChange_toggle" control_name="FSCommitForSaleOnChange"/> - + + + + Script Editor Font: + + + + + + + + + + + + + + + + + + + + + + + + + Preprocessor include path: + + + + + External Editor: + + + + + - - - - - - - Ruta include del preprocesador: - - + + + + + + + + + Constrain rotations to multiples of + + + + degrees, when not using 'snap to grid' + + + + + objects + + + + - - - - - - - - Constrain rotations to multiples of - - - - degrees, when not using 'snap to grid' - - - - - objects - - - - Actualmente establecido a: [ITEM] + + + Drop a script here. + + + Actualmente establecido a: [SCRIPT] + + + + + Punto de pivotaje - @@ -140,10 +150,6 @@ - - - - diff --git a/indra/newview/skins/default/xui/fr/panel_preferences_firestorm.xml b/indra/newview/skins/default/xui/fr/panel_preferences_firestorm.xml index e8affbbf45..6547e65c95 100644 --- a/indra/newview/skins/default/xui/fr/panel_preferences_firestorm.xml +++ b/indra/newview/skins/default/xui/fr/panel_preferences_firestorm.xml @@ -111,15 +111,10 @@ Glissez un objet ici. Actuellement : [ITEM] - Point de pivot - - - - - + + Drop a script here. + Actuellement: [SCRIPT]