Prevent concurrent access to AudioSendStream's configuration.
By design:
* OnPacketAdded() is meant to be called on pacer thread.
* Reconfigure() is meant to be called on worker thread.
Thus we guard against race condition on config_ member.
Possible downside: packet filtering based on ssrc might be slowed down.
Bug: webrtc:9849
Change-Id: I734bb9b34b01db160705897adb1b58e866e12639
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/146980
Commit-Queue: Yves Gerey <yvesg@google.com>
Reviewed-by: Oskar Sundbom <ossu@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28691}
diff --git a/audio/audio_send_stream.cc b/audio/audio_send_stream.cc
index 8eeacda..c5ec1b5 100644
--- a/audio/audio_send_stream.cc
+++ b/audio/audio_send_stream.cc
@@ -236,6 +236,8 @@
const auto& channel_send = stream->channel_send_;
const auto& old_config = stream->config_;
+ stream->config_cs_.Enter();
+
// Configuration parameters which cannot be changed.
RTC_DCHECK(first_time ||
old_config.send_transport == new_config.send_transport);
@@ -263,6 +265,9 @@
const ExtensionIds old_ids = FindExtensionIds(old_config.rtp.extensions);
const ExtensionIds new_ids = FindExtensionIds(new_config.rtp.extensions);
+
+ stream->config_cs_.Leave();
+
// Audio level indication
if (first_time || new_ids.audio_level != old_ids.audio_level) {
channel_send->SetSendAudioLevelIndicationStatus(new_ids.audio_level != 0,
@@ -299,6 +304,7 @@
stream->rtp_transport_, bandwidth_observer);
}
}
+ stream->config_cs_.Enter();
// MID RTP header extension.
if ((first_time || new_ids.mid != old_ids.mid ||
new_config.rtp.mid != old_config.rtp.mid) &&
@@ -321,6 +327,7 @@
ReconfigureBitrateObserver(stream, new_config);
}
stream->config_ = new_config;
+ stream->config_cs_.Leave();
}
void AudioSendStream::Start() {
@@ -487,7 +494,12 @@
void AudioSendStream::OnPacketAdded(uint32_t ssrc, uint16_t seq_num) {
RTC_DCHECK(pacer_thread_checker_.IsCurrent());
// Only packets that belong to this stream are of interest.
- if (ssrc == config_.rtp.ssrc) {
+ bool same_ssrc;
+ {
+ rtc::CritScope lock(&config_cs_);
+ same_ssrc = ssrc == config_.rtp.ssrc;
+ }
+ if (same_ssrc) {
rtc::CritScope lock(&packet_loss_tracker_cs_);
// TODO(eladalon): This function call could potentially reset the window,
// setting both PLR and RPLR to unknown. Consider (during upcoming
diff --git a/audio/audio_send_stream.h b/audio/audio_send_stream.h
index fd65296..37eb89a 100644
--- a/audio/audio_send_stream.h
+++ b/audio/audio_send_stream.h
@@ -153,6 +153,7 @@
rtc::RaceChecker audio_capture_race_checker_;
rtc::TaskQueue* worker_queue_;
const AudioAllocationSettings allocation_settings_;
+ rtc::CriticalSection config_cs_;
webrtc::AudioSendStream::Config config_;
rtc::scoped_refptr<webrtc::AudioState> audio_state_;
const std::unique_ptr<voe::ChannelSendInterface> channel_send_;
diff --git a/audio/audio_send_stream_unittest.cc b/audio/audio_send_stream_unittest.cc
index dcd3667..94bc34c 100644
--- a/audio/audio_send_stream_unittest.cc
+++ b/audio/audio_send_stream_unittest.cc
@@ -11,6 +11,7 @@
#include "audio/audio_send_stream.h"
#include <string>
+#include <thread>
#include <utility>
#include <vector>
@@ -679,6 +680,29 @@
send_stream->Reconfigure(helper.config());
}
+// Allow to check for race conditions under tsan.
+// This mimicks the situation where 'ModuleProcessThread' (pacer thread) is
+// launched by webrtc::RtpTransportControllerSend::RtpTransportControllerSend().
+TEST(AudioSendStreamTest, RaceFree) {
+ ConfigHelper helper(false, false);
+ // Sanity checks: copy-pasted from DontRecreateEncoder test.
+ EXPECT_CALL(*helper.channel_send(), SetEncoderForMock(_, _))
+ .WillOnce(Return());
+
+ EXPECT_CALL(*helper.channel_send(), RegisterCngPayloadType(105, 8000));
+
+ helper.config().send_codec_spec =
+ AudioSendStream::Config::SendCodecSpec(9, kG722Format);
+ helper.config().send_codec_spec->cng_payload_type = 105;
+ auto send_stream = helper.CreateAudioSendStream();
+ std::thread pacer([&]() {
+ send_stream->OnPacketAdded(/*ssrc*/ 0xcafe,
+ /*seq_num*/ 0xf00d);
+ });
+ send_stream->Reconfigure(helper.config());
+ pacer.join();
+}
+
TEST(AudioSendStreamTest, ReconfigureTransportCcResetsFirst) {
ScopedFieldTrials field_trials("WebRTC-Audio-SendSideBwe/Enabled/");
ConfigHelper helper(false, true);