diff options
| author | Erik Kundiman <erik@megapahit.org> | 2026-08-07 10:24:49 +0800 |
|---|---|---|
| committer | Erik Kundiman <erik@megapahit.org> | 2026-08-07 10:24:49 +0800 |
| commit | a20370141bb8840c24dbca18f4311aaf238348a8 (patch) | |
| tree | 41051d54b06d27e285da71d0f15fc4f139ffbc4d /indra/llwebrtc/llwebrtc.cpp | |
| parent | cdd2eaf40207117722d046fcc9c5707b884c4b49 (diff) | |
| parent | 0686467f42a06ec84368851f48f31beb43888df0 (diff) | |
Merge tag 'Second_Life_Release#0686467f-26.3' into 26.3
Diffstat (limited to 'indra/llwebrtc/llwebrtc.cpp')
| -rw-r--r-- | indra/llwebrtc/llwebrtc.cpp | 100 |
1 files changed, 82 insertions, 18 deletions
diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 613f3f1a7e..ef261168b4 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -26,6 +26,9 @@ #include "llwebrtc_impl.h" #include <algorithm> +#include <chrono> +#include <future> +#include <thread> #include <string.h> #include "api/audio/create_audio_device_module.h" #include "api/audio_codecs/audio_decoder_factory.h" @@ -411,8 +414,17 @@ void LLWebRTCImpl::init() void LLWebRTCImpl::terminate() { - mWorkerThread->BlockingCall( - [this]() + // Run all blocking WebRTC shutdown calls on a separate thread so that a + // hung BlockingCall cannot block the viewer shutdown indefinitely. + // Webrtc is not mission critical, we need to save personal data. + auto done_promise = std::make_shared<std::promise<void> >(); + std::future<void> done_future = done_promise->get_future(); + + std::thread shutdown_thread( + [this, done_promise]() mutable + { + mWorkerThread->BlockingCall( + [this]() { if (mDeviceModule) { @@ -421,27 +433,58 @@ void LLWebRTCImpl::terminate() } }); - for (auto &connection : mPeerConnections) - { - connection->terminate(); - } - - // connection->terminate() above spawns a number of Signaling thread calls to - // shut down the connection. The following Blocking Call will wait - // until they're done before it's executed, allowing time to clean up. + mSignalingThread->PostTask( + [this]() + { + for (auto& connection : mPeerConnections) + { + connection->terminate(); + } + }); - mSignalingThread->BlockingCall([this]() { mPeerConnectionFactory = nullptr; }); + // connection->terminate() above spawns a number of additional Signaling thread calls to + // shut down the connection. The following Blocking Call will wait + // until they're done before it's executed, allowing time to clean up. + mSignalingThread->BlockingCall([this]() { + mPeerConnectionFactory = nullptr; + }); - mWorkerThread->BlockingCall( - [this]() + mWorkerThread->BlockingCall( + [this]() { if (mDeviceModule) { mDeviceModule->ForceTerminate(); } - mDeviceModule = nullptr; + mDeviceModule = nullptr; }); + done_promise->set_value(); + }); + + constexpr auto WEBRTC_TERMINATE_TIMEOUT = std::chrono::seconds(10); + if (done_future.wait_for(WEBRTC_TERMINATE_TIMEOUT) == std::future_status::timeout) + { + RTC_LOG(LS_WARNING) << __FUNCTION__ + << ": timed out waiting for WebRTC thread shutdown." + " Detaching — some WebRTC resources will be leaked."; + shutdown_thread.detach(); + + // Release the unique_ptrs WITHOUT joining/deleting: the detached thread + // may still be using these thread objects. + // The raw pointers are intentionally leaked — the process is exiting anyway + // and our priority is saving cache and personal data. + (void)mNetworkThread.release(); + (void)mWorkerThread.release(); + (void)mSignalingThread.release(); + + mPeerConnections.clear(); + webrtc::LogMessage::RemoveLogToStream(mLogSink); + return; + } + + shutdown_thread.join(); + // In case peer connections still somehow have jobs in workers, // only clear connections up after clearing workers. mNetworkThread = nullptr; @@ -764,6 +807,7 @@ void LLWebRTCImpl::updateDevices() char name[webrtc::kAdmMaxDeviceNameSize]; char guid[webrtc::kAdmMaxGuidSize]; mDeviceModule->PlayoutDeviceName(index, name, guid); + RTC_LOG(LS_VERBOSE) << "updateDevices: playout device [" << index << "] name='" << name << "' guid='" << guid << "'"; mPlayoutDeviceList.emplace_back(name, guid); } @@ -782,9 +826,12 @@ void LLWebRTCImpl::updateDevices() char name[webrtc::kAdmMaxDeviceNameSize]; char guid[webrtc::kAdmMaxGuidSize]; mDeviceModule->RecordingDeviceName(index, name, guid); + RTC_LOG(LS_VERBOSE) << "updateDevices: recording device [" << index << "] name='" << name << "' guid='" << guid << "'"; mRecordingDeviceList.emplace_back(name, guid); } + RTC_LOG(LS_INFO) << "updateDevices, playout count: " << renderDeviceCount << "; capture count: " << captureDeviceCount; + for (auto &observer : mVoiceDevicesObserverList) { observer->OnDevicesChanged(mPlayoutDeviceList, mRecordingDeviceList); @@ -994,6 +1041,7 @@ LLWebRTCPeerConnectionImpl::LLWebRTCPeerConnectionImpl(const webrtc::Environment mAnswerReceived(false), mPeerConnectionState(webrtc::PeerConnectionInterface::PeerConnectionState::kNew), mDisconnectCount(0), + mStatsRequestPending(false), mPendingJobs(0) { } @@ -1793,16 +1841,32 @@ void LLWebRTCPeerConnectionImpl::gatherConnectionStats() return; } - auto stats_callback = webrtc::make_ref_counted<LLStatsCollectorCallback>( - [this](const LLWebRTCStatsMap& generic_stats) + webrtc::scoped_refptr<LLWebRTCPeerConnectionImpl> self(this); + mWebRTCImpl->PostSignalingTask( + [self]() + { + if (!self->mPeerConnection + || self->mPeerConnectionState != webrtc::PeerConnectionInterface::PeerConnectionState::kConnected + || self->mStatsRequestPending) // signaling thread only + { + return; + } + + self->mStatsRequestPending = true; + + auto stats_callback = webrtc::make_ref_counted<LLStatsCollectorCallback>( + [self](const LLWebRTCStatsMap& generic_stats) { - for (auto& observer : mSignalingObserverList) + self->mStatsRequestPending = false; + + for (auto& observer : self->mSignalingObserverList) { observer->OnStatsDelivered(generic_stats); } }); - mPeerConnection->GetStats(stats_callback.get()); + self->mPeerConnection->GetStats(stats_callback.get()); + }); } LLWebRTCImpl * gWebRTCImpl = nullptr; |
