NackModule2: coalesce repeating tasks.
NackModule2 creates repeating tasks, but as there are many modules (one per receiver) these tasks execute out of phase with each other, multipliying the amount of wakeups caused. Fix this by creating a single wakeup source that serves all NackModule2 instances in a call. Bug: webrtc:12989 Change-Id: Ia9c84307eb57349679e42b673474feb2cb43f08e Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/226464 Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Commit-Queue: Markus Handell <handellm@webrtc.org> Cr-Commit-Position: refs/heads/master@{#34527}
This commit is contained in:
committed by
WebRTC LUCI CQ
parent
41e98bab1e
commit
0e62f7aa98
@ -82,6 +82,7 @@ rtc_library("nack_module") {
|
||||
deps = [
|
||||
"..:module_api",
|
||||
"../../api:sequence_checker",
|
||||
"../../api/task_queue",
|
||||
"../../api/units:time_delta",
|
||||
"../../api/units:timestamp",
|
||||
"../../rtc_base:checks",
|
||||
|
||||
@ -13,6 +13,8 @@
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/experiments/field_trial_parser.h"
|
||||
@ -43,7 +45,52 @@ int64_t GetSendNackDelay() {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
constexpr TimeDelta NackModule2::kUpdateInterval;
|
||||
constexpr TimeDelta NackPeriodicProcessor::kUpdateInterval;
|
||||
|
||||
NackPeriodicProcessor::NackPeriodicProcessor(TimeDelta update_interval)
|
||||
: update_interval_(update_interval) {}
|
||||
|
||||
NackPeriodicProcessor::~NackPeriodicProcessor() {}
|
||||
|
||||
void NackPeriodicProcessor::RegisterNackModule(NackModuleBase* module) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_);
|
||||
modules_.push_back(module);
|
||||
if (modules_.size() != 1)
|
||||
return;
|
||||
repeating_task_ = RepeatingTaskHandle::DelayedStart(
|
||||
TaskQueueBase::Current(), update_interval_, [this] {
|
||||
RTC_DCHECK_RUN_ON(&sequence_);
|
||||
ProcessNackModules();
|
||||
return update_interval_;
|
||||
});
|
||||
}
|
||||
|
||||
void NackPeriodicProcessor::UnregisterNackModule(NackModuleBase* module) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_);
|
||||
auto it = std::find(modules_.begin(), modules_.end(), module);
|
||||
RTC_DCHECK(it != modules_.end());
|
||||
modules_.erase(it);
|
||||
if (modules_.empty())
|
||||
repeating_task_.Stop();
|
||||
}
|
||||
|
||||
// RTC_RUN_ON(sequence_)
|
||||
void NackPeriodicProcessor::ProcessNackModules() {
|
||||
for (NackModuleBase* module : modules_)
|
||||
module->ProcessNacks();
|
||||
}
|
||||
|
||||
ScopedNackPeriodicProcessorRegistration::
|
||||
ScopedNackPeriodicProcessorRegistration(NackModuleBase* module,
|
||||
NackPeriodicProcessor* processor)
|
||||
: module_(module), processor_(processor) {
|
||||
processor_->RegisterNackModule(module_);
|
||||
}
|
||||
|
||||
ScopedNackPeriodicProcessorRegistration::
|
||||
~ScopedNackPeriodicProcessorRegistration() {
|
||||
processor_->UnregisterNackModule(module_);
|
||||
}
|
||||
|
||||
NackModule2::NackInfo::NackInfo()
|
||||
: seq_num(0), send_at_seq_num(0), sent_at_time(-1), retries(0) {}
|
||||
@ -89,12 +136,11 @@ NackModule2::BackoffSettings::ParseFromFieldTrials() {
|
||||
}
|
||||
|
||||
NackModule2::NackModule2(TaskQueueBase* current_queue,
|
||||
NackPeriodicProcessor* periodic_processor,
|
||||
Clock* clock,
|
||||
NackSender* nack_sender,
|
||||
KeyFrameRequestSender* keyframe_request_sender,
|
||||
TimeDelta update_interval /*= kUpdateInterval*/)
|
||||
KeyFrameRequestSender* keyframe_request_sender)
|
||||
: worker_thread_(current_queue),
|
||||
update_interval_(update_interval),
|
||||
clock_(clock),
|
||||
nack_sender_(nack_sender),
|
||||
keyframe_request_sender_(keyframe_request_sender),
|
||||
@ -103,32 +149,27 @@ NackModule2::NackModule2(TaskQueueBase* current_queue,
|
||||
rtt_ms_(kDefaultRttMs),
|
||||
newest_seq_num_(0),
|
||||
send_nack_delay_ms_(GetSendNackDelay()),
|
||||
backoff_settings_(BackoffSettings::ParseFromFieldTrials()) {
|
||||
backoff_settings_(BackoffSettings::ParseFromFieldTrials()),
|
||||
processor_registration_(this, periodic_processor) {
|
||||
RTC_DCHECK(clock_);
|
||||
RTC_DCHECK(nack_sender_);
|
||||
RTC_DCHECK(keyframe_request_sender_);
|
||||
RTC_DCHECK_GT(update_interval.ms(), 0);
|
||||
RTC_DCHECK(worker_thread_);
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
|
||||
repeating_task_ = RepeatingTaskHandle::DelayedStart(
|
||||
TaskQueueBase::Current(), update_interval_,
|
||||
[this]() {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
std::vector<uint16_t> nack_batch = GetNackBatch(kTimeOnly);
|
||||
if (!nack_batch.empty()) {
|
||||
// This batch of NACKs is triggered externally; there is no external
|
||||
// initiator who can batch them with other feedback messages.
|
||||
nack_sender_->SendNack(nack_batch, /*buffering_allowed=*/false);
|
||||
}
|
||||
return update_interval_;
|
||||
},
|
||||
clock_);
|
||||
}
|
||||
|
||||
NackModule2::~NackModule2() {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
repeating_task_.Stop();
|
||||
}
|
||||
|
||||
void NackModule2::ProcessNacks() {
|
||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||
std::vector<uint16_t> nack_batch = GetNackBatch(kTimeOnly);
|
||||
if (!nack_batch.empty()) {
|
||||
// This batch of NACKs is triggered externally; there is no external
|
||||
// initiator who can batch them with other feedback messages.
|
||||
nack_sender_->SendNack(nack_batch, /*buffering_allowed=*/false);
|
||||
}
|
||||
}
|
||||
|
||||
int NackModule2::OnReceivedPacket(uint16_t seq_num, bool is_keyframe) {
|
||||
|
||||
@ -30,20 +30,54 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class NackModuleBase {
|
||||
public:
|
||||
virtual ~NackModuleBase() = default;
|
||||
virtual void ProcessNacks() = 0;
|
||||
};
|
||||
|
||||
class NackPeriodicProcessor {
|
||||
public:
|
||||
static constexpr TimeDelta kUpdateInterval = TimeDelta::Millis(20);
|
||||
explicit NackPeriodicProcessor(TimeDelta update_interval = kUpdateInterval);
|
||||
~NackPeriodicProcessor();
|
||||
void RegisterNackModule(NackModuleBase* module);
|
||||
void UnregisterNackModule(NackModuleBase* module);
|
||||
|
||||
private:
|
||||
void ProcessNackModules() RTC_RUN_ON(sequence_);
|
||||
|
||||
const TimeDelta update_interval_;
|
||||
RepeatingTaskHandle repeating_task_ RTC_GUARDED_BY(sequence_);
|
||||
std::vector<NackModuleBase*> modules_ RTC_GUARDED_BY(sequence_);
|
||||
RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_;
|
||||
};
|
||||
|
||||
class ScopedNackPeriodicProcessorRegistration {
|
||||
public:
|
||||
ScopedNackPeriodicProcessorRegistration(NackModuleBase* module,
|
||||
NackPeriodicProcessor* processor);
|
||||
~ScopedNackPeriodicProcessorRegistration();
|
||||
|
||||
private:
|
||||
NackModuleBase* const module_;
|
||||
NackPeriodicProcessor* const processor_;
|
||||
};
|
||||
|
||||
// TODO(bugs.webrtc.org/11594): This class no longer implements the Module
|
||||
// interface and therefore "NackModule" may not be a descriptive name anymore.
|
||||
// Consider renaming to e.g. NackTracker or NackRequester.
|
||||
class NackModule2 final {
|
||||
class NackModule2 final : public NackModuleBase {
|
||||
public:
|
||||
static constexpr TimeDelta kUpdateInterval = TimeDelta::Millis(20);
|
||||
|
||||
NackModule2(TaskQueueBase* current_queue,
|
||||
NackPeriodicProcessor* periodic_processor,
|
||||
Clock* clock,
|
||||
NackSender* nack_sender,
|
||||
KeyFrameRequestSender* keyframe_request_sender,
|
||||
TimeDelta update_interval = kUpdateInterval);
|
||||
KeyFrameRequestSender* keyframe_request_sender);
|
||||
~NackModule2();
|
||||
|
||||
void ProcessNacks() override;
|
||||
|
||||
int OnReceivedPacket(uint16_t seq_num, bool is_keyframe);
|
||||
int OnReceivedPacket(uint16_t seq_num, bool is_keyframe, bool is_recovered);
|
||||
|
||||
@ -103,11 +137,6 @@ class NackModule2 final {
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(worker_thread_);
|
||||
|
||||
TaskQueueBase* const worker_thread_;
|
||||
|
||||
// Used to regularly call SendNack if needed.
|
||||
RepeatingTaskHandle repeating_task_ RTC_GUARDED_BY(worker_thread_);
|
||||
const TimeDelta update_interval_;
|
||||
|
||||
Clock* const clock_;
|
||||
NackSender* const nack_sender_;
|
||||
KeyFrameRequestSender* const keyframe_request_sender_;
|
||||
@ -131,6 +160,8 @@ class NackModule2 final {
|
||||
|
||||
const absl::optional<BackoffSettings> backoff_settings_;
|
||||
|
||||
ScopedNackPeriodicProcessorRegistration processor_registration_;
|
||||
|
||||
// Used to signal destruction to potentially pending tasks.
|
||||
ScopedTaskSafety task_safety_;
|
||||
};
|
||||
|
||||
@ -80,10 +80,13 @@ class TestNackModule2 : public ::testing::TestWithParam<bool>,
|
||||
}
|
||||
|
||||
NackModule2& CreateNackModule(
|
||||
TimeDelta interval = NackModule2::kUpdateInterval) {
|
||||
TimeDelta interval = NackPeriodicProcessor::kUpdateInterval) {
|
||||
RTC_DCHECK(!nack_module_.get());
|
||||
nack_module_ = std::make_unique<NackModule2>(
|
||||
TaskQueueBase::Current(), clock_.get(), this, this, interval);
|
||||
nack_periodic_processor_ =
|
||||
std::make_unique<NackPeriodicProcessor>(interval);
|
||||
nack_module_ = std::make_unique<NackModule2>(TaskQueueBase::Current(),
|
||||
nack_periodic_processor_.get(),
|
||||
clock_.get(), this, this);
|
||||
nack_module_->UpdateRtt(kDefaultRttMs);
|
||||
return *nack_module_.get();
|
||||
}
|
||||
@ -92,6 +95,7 @@ class TestNackModule2 : public ::testing::TestWithParam<bool>,
|
||||
test::RunLoop loop_;
|
||||
std::unique_ptr<SimulatedClock> clock_;
|
||||
test::ScopedFieldTrials field_trial_;
|
||||
std::unique_ptr<NackPeriodicProcessor> nack_periodic_processor_;
|
||||
std::unique_ptr<NackModule2> nack_module_;
|
||||
std::vector<uint16_t> sent_nacks_;
|
||||
int keyframes_requested_;
|
||||
@ -379,7 +383,11 @@ class TestNackModule2WithFieldTrial : public ::testing::Test,
|
||||
TestNackModule2WithFieldTrial()
|
||||
: nack_delay_field_trial_("WebRTC-SendNackDelayMs/10/"),
|
||||
clock_(new SimulatedClock(0)),
|
||||
nack_module_(TaskQueueBase::Current(), clock_.get(), this, this),
|
||||
nack_module_(TaskQueueBase::Current(),
|
||||
&nack_periodic_processor_,
|
||||
clock_.get(),
|
||||
this,
|
||||
this),
|
||||
keyframes_requested_(0) {}
|
||||
|
||||
void SendNack(const std::vector<uint16_t>& sequence_numbers,
|
||||
@ -392,6 +400,7 @@ class TestNackModule2WithFieldTrial : public ::testing::Test,
|
||||
|
||||
test::ScopedFieldTrials nack_delay_field_trial_;
|
||||
std::unique_ptr<SimulatedClock> clock_;
|
||||
NackPeriodicProcessor nack_periodic_processor_;
|
||||
NackModule2 nack_module_;
|
||||
std::vector<uint16_t> sent_nacks_;
|
||||
int keyframes_requested_;
|
||||
|
||||
Reference in New Issue
Block a user