summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Kleshchev <117672381+akleshchev@users.noreply.github.com>2026-08-20 22:47:07 +0300
committerAndrey Kleshchev <117672381+akleshchev@users.noreply.github.com>2026-08-21 14:30:37 +0300
commitd43bff52cdc6a6652d16b5d843ad6df310fa58e0 (patch)
tree7ad9ff6144a082bc2da03aa2674587d8e139e8e9
parentfcbd59ea37f15c9bf800ded2b1ea8e95c2cc4f2e (diff)
#6123 Fix worker thread crash on termination
-rw-r--r--indra/llwebrtc/llwebrtc.cpp62
1 files changed, 41 insertions, 21 deletions
diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp
index 3b5d338896..d94b91c0ff 100644
--- a/indra/llwebrtc/llwebrtc.cpp
+++ b/indra/llwebrtc/llwebrtc.cpp
@@ -410,8 +410,27 @@ bool LLWebRTCImpl::terminate()
std::vector<webrtc::scoped_refptr<LLWebRTCPeerConnectionImpl>> connections;
connections.swap(mPeerConnections);
+ // Explicitely unregister observers before shutting down the threads.
+ // shutdown_thread, if detached, can outlive observer.
+ mWorkerThread->BlockingCall([this]()
+ {
+ if (mDeviceModule)
+ {
+ mDeviceModule->SetObserver(nullptr);
+ }
+ });
+ mVoiceDevicesObserverList.clear();
+
+ // shutdown_thread can be detached, then LLWebRTCImpl will be nulled out.
+ // Capture what's needed in lambda, don't rely on [this].
std::thread shutdown_thread(
- [this, connections = std::move(connections), done_promise]() mutable
+ [networkThread = std::move(mNetworkThread),
+ workerThread = std::move(mWorkerThread),
+ signalingThread = std::move(mSignalingThread),
+ deviceModule = std::move(mDeviceModule),
+ factory = std::move(mPeerConnectionFactory),
+ connections = std::move(connections),
+ done_promise]() mutable
{
// Stop the capture/render devices alongside the connection teardown
// below rather than ahead of it. Both of these calls end in a
@@ -433,13 +452,13 @@ bool LLWebRTCImpl::terminate()
// of this lambda can't run until this task has finished. Any
// worker-thread work the signaling close does is likewise ordered
// after it, so nothing sees the device module half torn down.
- mWorkerThread->PostTask(
- [this]()
+ workerThread->PostTask(
+ [&deviceModule]()
{
- if (mDeviceModule)
+ if (deviceModule)
{
- mDeviceModule->ForceStopRecording();
- mDeviceModule->StopPlayout();
+ deviceModule->ForceStopRecording();
+ deviceModule->StopPlayout();
}
});
@@ -454,7 +473,7 @@ bool LLWebRTCImpl::terminate()
// callback inline, and that callback calls back into the viewer's
// signaling observers. Those observers are only valid until
// llwebrtc::terminate() returns.
- mSignalingThread->BlockingCall(
+ signalingThread->BlockingCall(
[&connections]()
{
for (auto& connection : connections)
@@ -467,22 +486,29 @@ bool LLWebRTCImpl::terminate()
});
// Drain anything the closes posted before dropping the factory.
- mSignalingThread->BlockingCall([]() {});
+ signalingThread->BlockingCall([]() {});
- mSignalingThread->BlockingCall([this]() {
- mPeerConnectionFactory = nullptr;
+ signalingThread->BlockingCall([&factory]() {
+ factory = nullptr;
});
- mWorkerThread->BlockingCall(
- [this]()
+ workerThread->BlockingCall(
+ [&deviceModule]()
{
- if (mDeviceModule)
+ if (deviceModule)
{
- mDeviceModule->ForceTerminate();
+ deviceModule->ForceTerminate();
}
- mDeviceModule = nullptr;
+ deviceModule = nullptr;
});
+ // Explicitly clean WebRTC threads in dependency order before signalling completion.
+ // The connections were closed and destroyed on the signaling thread, so it's safe
+ // to clean.
+ signalingThread.reset();
+ workerThread.reset();
+ networkThread.reset();
+
done_promise->set_value();
});
@@ -515,12 +541,6 @@ bool LLWebRTCImpl::terminate()
shutdown_thread.join();
- // The connections were closed and destroyed on the signaling thread before
- // the shutdown thread finished, so it's safe to drop the threads now.
- mNetworkThread = nullptr;
- mWorkerThread = nullptr;
- mSignalingThread = nullptr;
-
webrtc::LogMessage::RemoveLogToStream(mLogSink);
return true;
}