Adds TCP fairness test to GoogCC.
Bug: webrtc:9883 Change-Id: Ie78e51edb08f6c22dbf02168b1d3b067b2c0c55e Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/140293 Commit-Queue: Sebastian Jansson <srte@webrtc.org> Reviewed-by: Björn Terelius <terelius@webrtc.org> Cr-Commit-Position: refs/heads/master@{#28193}
This commit is contained in:

committed by
Commit Bot

parent
26b5e35276
commit
b13ccc5288
@ -685,5 +685,29 @@ TEST_F(GoogCcNetworkControllerTest, NoRttBackoffCollapseWhenVideoStops) {
|
||||
EXPECT_GT(client->send_bandwidth().kbps(), 1000);
|
||||
}
|
||||
|
||||
TEST_F(GoogCcNetworkControllerTest, IsFairToTCP) {
|
||||
Scenario s("googcc_unit/tcp_fairness");
|
||||
NetworkSimulationConfig net_conf;
|
||||
net_conf.bandwidth = DataRate::kbps(1000);
|
||||
net_conf.delay = TimeDelta::ms(50);
|
||||
auto* client = s.CreateClient("send", [&](CallClientConfig* c) {
|
||||
c->transport.rates.start_rate = DataRate::kbps(1000);
|
||||
});
|
||||
auto send_net = {s.CreateSimulationNode(net_conf)};
|
||||
auto ret_net = {s.CreateSimulationNode(net_conf)};
|
||||
auto* route = s.CreateRoutes(
|
||||
client, send_net, s.CreateClient("return", CallClientConfig()), ret_net);
|
||||
s.CreateVideoStream(route->forward(), VideoStreamConfig());
|
||||
s.net()->StartFakeTcpCrossTraffic(s.net()->CreateRoute(send_net),
|
||||
s.net()->CreateRoute(ret_net),
|
||||
FakeTcpConfig());
|
||||
s.RunFor(TimeDelta::seconds(10));
|
||||
|
||||
// Currently only testing for the upper limit as we in practice back out
|
||||
// quite a lot in this scenario. If this behavior is fixed, we should add a
|
||||
// lower bound to ensure it stays fixed.
|
||||
EXPECT_LT(client->send_bandwidth().kbps(), 750);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
@ -114,5 +114,70 @@ ColumnPrinter PulsedPeaksCrossTraffic::StatsPrinter() {
|
||||
32);
|
||||
}
|
||||
|
||||
FakeTcpCrossTraffic::FakeTcpCrossTraffic(FakeTcpConfig config,
|
||||
EmulatedRoute* send_route,
|
||||
EmulatedRoute* ret_route)
|
||||
: conf_(config), route_(this, send_route, ret_route) {}
|
||||
|
||||
void FakeTcpCrossTraffic::Process(Timestamp at_time) {
|
||||
SendPackets(at_time);
|
||||
}
|
||||
|
||||
void FakeTcpCrossTraffic::OnRequest(int sequence_number, Timestamp at_time) {
|
||||
const size_t kAckPacketSize = 20;
|
||||
route_.SendResponse(kAckPacketSize, sequence_number);
|
||||
}
|
||||
|
||||
void FakeTcpCrossTraffic::OnResponse(int sequence_number, Timestamp at_time) {
|
||||
ack_received_ = true;
|
||||
auto it = in_flight_.find(sequence_number);
|
||||
if (it != in_flight_.end()) {
|
||||
last_rtt_ = at_time - in_flight_.at(sequence_number);
|
||||
in_flight_.erase(sequence_number);
|
||||
}
|
||||
if (sequence_number - last_acked_seq_num_ > 1) {
|
||||
HandleLoss(at_time);
|
||||
} else if (cwnd_ <= ssthresh_) {
|
||||
cwnd_ += 1;
|
||||
} else {
|
||||
cwnd_ += 1.0f / cwnd_;
|
||||
}
|
||||
last_acked_seq_num_ = std::max(sequence_number, last_acked_seq_num_);
|
||||
SendPackets(at_time);
|
||||
}
|
||||
|
||||
void FakeTcpCrossTraffic::HandleLoss(Timestamp at_time) {
|
||||
if (at_time - last_reduction_time_ < last_rtt_)
|
||||
return;
|
||||
last_reduction_time_ = at_time;
|
||||
ssthresh_ = std::max(static_cast<int>(in_flight_.size() / 2), 2);
|
||||
cwnd_ = ssthresh_;
|
||||
}
|
||||
|
||||
void FakeTcpCrossTraffic::SendPackets(Timestamp at_time) {
|
||||
int cwnd = std::ceil(cwnd_);
|
||||
int packets_to_send = std::max(cwnd - static_cast<int>(in_flight_.size()), 0);
|
||||
bool timeouts = false;
|
||||
for (auto it = in_flight_.begin(); it != in_flight_.end();) {
|
||||
if (it->second < at_time - conf_.packet_timeout) {
|
||||
it = in_flight_.erase(it);
|
||||
timeouts = true;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
if (timeouts)
|
||||
HandleLoss(at_time);
|
||||
for (int i = 0; i < packets_to_send; ++i) {
|
||||
if ((total_sent_ + conf_.packet_size) > conf_.send_limit) {
|
||||
break;
|
||||
}
|
||||
in_flight_.insert({next_sequence_number_, at_time});
|
||||
route_.SendRequest(conf_.packet_size.bytes<size_t>(),
|
||||
next_sequence_number_++);
|
||||
total_sent_ += conf_.packet_size;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
@ -90,6 +90,43 @@ class PulsedPeaksCrossTraffic {
|
||||
bool sending_ RTC_GUARDED_BY(sequence_checker_) = false;
|
||||
};
|
||||
|
||||
struct FakeTcpConfig {
|
||||
DataSize packet_size = DataSize::bytes(1200);
|
||||
DataSize send_limit = DataSize::PlusInfinity();
|
||||
int packet_window;
|
||||
TimeDelta process_interval = TimeDelta::ms(200);
|
||||
TimeDelta packet_timeout = TimeDelta::seconds(1);
|
||||
};
|
||||
|
||||
class FakeTcpCrossTraffic
|
||||
: public TwoWayFakeTrafficRoute<int, int>::TrafficHandlerInterface {
|
||||
public:
|
||||
FakeTcpCrossTraffic(FakeTcpConfig config,
|
||||
EmulatedRoute* send_route,
|
||||
EmulatedRoute* ret_route);
|
||||
void Process(Timestamp at_time);
|
||||
void OnRequest(int sequence_number, Timestamp at_time) override;
|
||||
void OnResponse(int sequence_number, Timestamp at_time) override;
|
||||
|
||||
void HandleLoss(Timestamp at_time);
|
||||
|
||||
void SendPackets(Timestamp at_time);
|
||||
|
||||
private:
|
||||
const FakeTcpConfig conf_;
|
||||
TwoWayFakeTrafficRoute<int, int> route_;
|
||||
|
||||
std::map<int, Timestamp> in_flight_;
|
||||
double cwnd_ = 10;
|
||||
double ssthresh_ = INFINITY;
|
||||
bool ack_received_ = false;
|
||||
int last_acked_seq_num_ = 0;
|
||||
int next_sequence_number_ = 0;
|
||||
Timestamp last_reduction_time_ = Timestamp::MinusInfinity();
|
||||
TimeDelta last_rtt_ = TimeDelta::Zero();
|
||||
DataSize total_sent_ = DataSize::Zero();
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
||||
|
@ -21,29 +21,11 @@ FeedbackGeneratorImpl::FeedbackGeneratorImpl(
|
||||
net_{&time_controller_},
|
||||
send_link_{new SimulatedNetwork(conf_.send_link)},
|
||||
ret_link_{new SimulatedNetwork(conf_.return_link)},
|
||||
send_ep_{net_.CreateEndpoint(EmulatedEndpointConfig())},
|
||||
ret_ep_{net_.CreateEndpoint(EmulatedEndpointConfig())},
|
||||
send_route_{net_.CreateRoute(
|
||||
send_ep_,
|
||||
{net_.CreateEmulatedNode(absl::WrapUnique(send_link_))},
|
||||
ret_ep_)},
|
||||
ret_route_{net_.CreateRoute(
|
||||
ret_ep_,
|
||||
{net_.CreateEmulatedNode(absl::WrapUnique(ret_link_))},
|
||||
send_ep_)},
|
||||
send_addr_{rtc::SocketAddress(send_ep_->GetPeerLocalAddress(), 0)},
|
||||
ret_addr_{rtc::SocketAddress(ret_ep_->GetPeerLocalAddress(), 0)},
|
||||
received_packet_handler_{send_route_,
|
||||
[&](SentPacket packet, Timestamp arrival_time) {
|
||||
OnPacketReceived(std::move(packet),
|
||||
arrival_time);
|
||||
}},
|
||||
received_feedback_handler_{
|
||||
ret_route_,
|
||||
[&](TransportPacketsFeedback packet, Timestamp arrival_time) {
|
||||
packet.feedback_time = arrival_time;
|
||||
feedback_.push_back(packet);
|
||||
}} {}
|
||||
route_(this,
|
||||
net_.CreateRoute(
|
||||
{net_.CreateEmulatedNode(absl::WrapUnique(send_link_))}),
|
||||
net_.CreateRoute(
|
||||
{net_.CreateEmulatedNode(absl::WrapUnique(ret_link_))})) {}
|
||||
|
||||
Timestamp FeedbackGeneratorImpl::Now() {
|
||||
return Timestamp::ms(time_controller_.GetClock()->TimeInMilliseconds());
|
||||
@ -57,7 +39,7 @@ void FeedbackGeneratorImpl::SendPacket(size_t size) {
|
||||
SentPacket sent;
|
||||
sent.send_time = Now();
|
||||
sent.size = DataSize::bytes(size);
|
||||
received_packet_handler_.SendPacket(send_ep_, size, sent);
|
||||
route_.SendRequest(size, sent);
|
||||
}
|
||||
|
||||
std::vector<TransportPacketsFeedback> FeedbackGeneratorImpl::PopFeedback() {
|
||||
@ -82,18 +64,23 @@ void FeedbackGeneratorImpl::SetSendLinkCapacity(DataRate capacity) {
|
||||
send_link_->SetConfig(conf_.send_link);
|
||||
}
|
||||
|
||||
void FeedbackGeneratorImpl::OnPacketReceived(SentPacket packet,
|
||||
Timestamp arrival_time) {
|
||||
void FeedbackGeneratorImpl::OnRequest(SentPacket packet,
|
||||
Timestamp arrival_time) {
|
||||
PacketResult result;
|
||||
result.sent_packet = packet;
|
||||
result.receive_time = arrival_time;
|
||||
builder_.packet_feedbacks.push_back(result);
|
||||
Timestamp first_recv = builder_.packet_feedbacks.front().receive_time;
|
||||
if (Now() - first_recv > conf_.feedback_interval) {
|
||||
received_feedback_handler_.SendPacket(
|
||||
ret_ep_, conf_.feedback_packet_size.bytes<size_t>(), builder_);
|
||||
route_.SendResponse(conf_.feedback_packet_size.bytes<size_t>(), builder_);
|
||||
builder_ = {};
|
||||
}
|
||||
}
|
||||
|
||||
void FeedbackGeneratorImpl::OnResponse(TransportPacketsFeedback packet,
|
||||
Timestamp arrival_time) {
|
||||
packet.feedback_time = arrival_time;
|
||||
feedback_.push_back(packet);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -22,42 +22,10 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
template <typename FakePacketType>
|
||||
class FakePacketRoute : public EmulatedNetworkReceiverInterface {
|
||||
public:
|
||||
FakePacketRoute(EmulatedRoute* route,
|
||||
std::function<void(FakePacketType, Timestamp)> action)
|
||||
: route_(route),
|
||||
action_(std::move(action)),
|
||||
send_addr_(route_->from->GetPeerLocalAddress(), 0),
|
||||
recv_addr_(route_->to->GetPeerLocalAddress(),
|
||||
*route_->to->BindReceiver(0, this)) {}
|
||||
|
||||
void SendPacket(EmulatedEndpoint* from, size_t size, FakePacketType packet) {
|
||||
RTC_CHECK_GE(size, sizeof(int));
|
||||
sent_.emplace(next_packet_id_, packet);
|
||||
rtc::CopyOnWriteBuffer buf(size);
|
||||
reinterpret_cast<int*>(buf.data())[0] = next_packet_id_++;
|
||||
from->SendPacket(send_addr_, recv_addr_, buf);
|
||||
}
|
||||
|
||||
void OnPacketReceived(EmulatedIpPacket packet) override {
|
||||
int packet_id = reinterpret_cast<int*>(packet.data.data())[0];
|
||||
action_(std::move(sent_[packet_id]), packet.arrival_time);
|
||||
sent_.erase(packet_id);
|
||||
}
|
||||
|
||||
private:
|
||||
EmulatedRoute* const route_;
|
||||
const std::function<void(FakePacketType, Timestamp)> action_;
|
||||
const rtc::SocketAddress send_addr_;
|
||||
const rtc::SocketAddress recv_addr_;
|
||||
|
||||
int next_packet_id_ = 0;
|
||||
std::map<int, FakePacketType> sent_;
|
||||
};
|
||||
|
||||
class FeedbackGeneratorImpl : public FeedbackGenerator {
|
||||
class FeedbackGeneratorImpl
|
||||
: public FeedbackGenerator,
|
||||
public TwoWayFakeTrafficRoute<SentPacket, TransportPacketsFeedback>::
|
||||
TrafficHandlerInterface {
|
||||
public:
|
||||
explicit FeedbackGeneratorImpl(Config config);
|
||||
Timestamp Now() override;
|
||||
@ -70,21 +38,17 @@ class FeedbackGeneratorImpl : public FeedbackGenerator {
|
||||
|
||||
void SetSendLinkCapacity(DataRate capacity) override;
|
||||
|
||||
void OnRequest(SentPacket packet, Timestamp arrival_time) override;
|
||||
void OnResponse(TransportPacketsFeedback packet,
|
||||
Timestamp arrival_time) override;
|
||||
|
||||
private:
|
||||
void OnPacketReceived(SentPacket packet, Timestamp arrival_time);
|
||||
Config conf_;
|
||||
GlobalSimulatedTimeController time_controller_;
|
||||
::webrtc::test::NetworkEmulationManagerImpl net_;
|
||||
SimulatedNetwork* const send_link_;
|
||||
SimulatedNetwork* const ret_link_;
|
||||
EmulatedEndpoint* const send_ep_;
|
||||
EmulatedEndpoint* const ret_ep_;
|
||||
EmulatedRoute* const send_route_;
|
||||
EmulatedRoute* const ret_route_;
|
||||
const rtc::SocketAddress send_addr_;
|
||||
const rtc::SocketAddress ret_addr_;
|
||||
FakePacketRoute<SentPacket> received_packet_handler_;
|
||||
FakePacketRoute<TransportPacketsFeedback> received_feedback_handler_;
|
||||
TwoWayFakeTrafficRoute<SentPacket, TransportPacketsFeedback> route_;
|
||||
|
||||
TransportPacketsFeedback builder_;
|
||||
std::vector<TransportPacketsFeedback> feedback_;
|
||||
|
@ -233,10 +233,9 @@ class EmulatedRoute {
|
||||
EmulatedEndpoint* to;
|
||||
bool active;
|
||||
};
|
||||
|
||||
class EndpointsContainer {
|
||||
public:
|
||||
EndpointsContainer(const std::vector<EmulatedEndpoint*>& endpoints);
|
||||
explicit EndpointsContainer(const std::vector<EmulatedEndpoint*>& endpoints);
|
||||
|
||||
EmulatedEndpoint* LookupByLocalAddress(const rtc::IPAddress& local_ip) const;
|
||||
bool HasEndpoint(EmulatedEndpoint* endpoint) const;
|
||||
@ -249,6 +248,73 @@ class EndpointsContainer {
|
||||
const std::vector<EmulatedEndpoint*> endpoints_;
|
||||
};
|
||||
|
||||
template <typename FakePacketType>
|
||||
class FakePacketRoute : public EmulatedNetworkReceiverInterface {
|
||||
public:
|
||||
FakePacketRoute(EmulatedRoute* route,
|
||||
std::function<void(FakePacketType, Timestamp)> action)
|
||||
: route_(route),
|
||||
action_(std::move(action)),
|
||||
send_addr_(route_->from->GetPeerLocalAddress(), 0),
|
||||
recv_addr_(route_->to->GetPeerLocalAddress(),
|
||||
*route_->to->BindReceiver(0, this)) {}
|
||||
|
||||
void SendPacket(size_t size, FakePacketType packet) {
|
||||
RTC_CHECK_GE(size, sizeof(int));
|
||||
sent_.emplace(next_packet_id_, packet);
|
||||
rtc::CopyOnWriteBuffer buf(size);
|
||||
reinterpret_cast<int*>(buf.data())[0] = next_packet_id_++;
|
||||
route_->from->SendPacket(send_addr_, recv_addr_, buf);
|
||||
}
|
||||
|
||||
void OnPacketReceived(EmulatedIpPacket packet) override {
|
||||
int packet_id = reinterpret_cast<int*>(packet.data.data())[0];
|
||||
action_(std::move(sent_[packet_id]), packet.arrival_time);
|
||||
sent_.erase(packet_id);
|
||||
}
|
||||
|
||||
private:
|
||||
EmulatedRoute* const route_;
|
||||
const std::function<void(FakePacketType, Timestamp)> action_;
|
||||
const rtc::SocketAddress send_addr_;
|
||||
const rtc::SocketAddress recv_addr_;
|
||||
int next_packet_id_ = 0;
|
||||
std::map<int, FakePacketType> sent_;
|
||||
};
|
||||
|
||||
template <typename RequestPacketType, typename ResponsePacketType>
|
||||
class TwoWayFakeTrafficRoute {
|
||||
public:
|
||||
class TrafficHandlerInterface {
|
||||
public:
|
||||
virtual void OnRequest(RequestPacketType, Timestamp) = 0;
|
||||
virtual void OnResponse(ResponsePacketType, Timestamp) = 0;
|
||||
virtual ~TrafficHandlerInterface() = default;
|
||||
};
|
||||
TwoWayFakeTrafficRoute(TrafficHandlerInterface* handler,
|
||||
EmulatedRoute* send_route,
|
||||
EmulatedRoute* ret_route)
|
||||
: handler_(handler),
|
||||
request_handler_{send_route,
|
||||
[&](RequestPacketType packet, Timestamp arrival_time) {
|
||||
handler_->OnRequest(std::move(packet), arrival_time);
|
||||
}},
|
||||
response_handler_{
|
||||
ret_route, [&](ResponsePacketType packet, Timestamp arrival_time) {
|
||||
handler_->OnResponse(std::move(packet), arrival_time);
|
||||
}} {}
|
||||
void SendRequest(size_t size, RequestPacketType packet) {
|
||||
request_handler_.SendPacket(size, std::move(packet));
|
||||
}
|
||||
void SendResponse(size_t size, ResponsePacketType packet) {
|
||||
response_handler_.SendPacket(size, std::move(packet));
|
||||
}
|
||||
|
||||
private:
|
||||
TrafficHandlerInterface* handler_;
|
||||
FakePacketRoute<RequestPacketType> request_handler_;
|
||||
FakePacketRoute<ResponsePacketType> response_handler_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // TEST_SCENARIO_NETWORK_NETWORK_EMULATION_H_
|
||||
|
@ -145,6 +145,13 @@ EmulatedRoute* NetworkEmulationManagerImpl::CreateRoute(
|
||||
return out;
|
||||
}
|
||||
|
||||
EmulatedRoute* NetworkEmulationManagerImpl::CreateRoute(
|
||||
const std::vector<EmulatedNetworkNode*>& via_nodes) {
|
||||
EmulatedEndpoint* from = CreateEndpoint(EmulatedEndpointConfig());
|
||||
EmulatedEndpoint* to = CreateEndpoint(EmulatedEndpointConfig());
|
||||
return CreateRoute(from, via_nodes, to);
|
||||
}
|
||||
|
||||
void NetworkEmulationManagerImpl::ClearRoute(EmulatedRoute* route) {
|
||||
RTC_CHECK(route->active) << "Route already cleared";
|
||||
task_queue_.SendTask([route]() {
|
||||
@ -223,6 +230,24 @@ NetworkEmulationManagerImpl::CreatePulsedPeaksCrossTraffic(
|
||||
return out;
|
||||
}
|
||||
|
||||
void NetworkEmulationManagerImpl::StartFakeTcpCrossTraffic(
|
||||
EmulatedRoute* send_route,
|
||||
EmulatedRoute* ret_route,
|
||||
FakeTcpConfig config) {
|
||||
task_queue_.PostTask([=]() {
|
||||
auto traffic =
|
||||
absl::make_unique<FakeTcpCrossTraffic>(config, send_route, ret_route);
|
||||
auto* traffic_ptr = traffic.get();
|
||||
tcp_cross_traffics_.push_back(std::move(traffic));
|
||||
TimeDelta process_interval = config.process_interval;
|
||||
RepeatingTaskHandle::Start(task_queue_.Get(),
|
||||
[this, process_interval, traffic_ptr] {
|
||||
traffic_ptr->Process(Now());
|
||||
return process_interval;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
EmulatedNetworkManagerInterface*
|
||||
NetworkEmulationManagerImpl::CreateEmulatedNetworkManagerInterface(
|
||||
const std::vector<EmulatedEndpoint*>& endpoints) {
|
||||
|
@ -51,6 +51,10 @@ class NetworkEmulationManagerImpl : public NetworkEmulationManager {
|
||||
EmulatedRoute* CreateRoute(EmulatedEndpoint* from,
|
||||
const std::vector<EmulatedNetworkNode*>& via_nodes,
|
||||
EmulatedEndpoint* to) override;
|
||||
|
||||
EmulatedRoute* CreateRoute(
|
||||
const std::vector<EmulatedNetworkNode*>& via_nodes);
|
||||
|
||||
void ClearRoute(EmulatedRoute* route) override;
|
||||
|
||||
TrafficRoute* CreateTrafficRoute(
|
||||
@ -61,6 +65,9 @@ class NetworkEmulationManagerImpl : public NetworkEmulationManager {
|
||||
PulsedPeaksCrossTraffic* CreatePulsedPeaksCrossTraffic(
|
||||
TrafficRoute* traffic_route,
|
||||
PulsedPeaksConfig config);
|
||||
void StartFakeTcpCrossTraffic(EmulatedRoute* send_route,
|
||||
EmulatedRoute* ret_route,
|
||||
FakeTcpConfig config);
|
||||
|
||||
EmulatedNetworkManagerInterface* CreateEmulatedNetworkManagerInterface(
|
||||
const std::vector<EmulatedEndpoint*>& endpoints) override;
|
||||
@ -84,6 +91,7 @@ class NetworkEmulationManagerImpl : public NetworkEmulationManager {
|
||||
std::vector<std::unique_ptr<TrafficRoute>> traffic_routes_;
|
||||
std::vector<std::unique_ptr<RandomWalkCrossTraffic>> random_cross_traffics_;
|
||||
std::vector<std::unique_ptr<PulsedPeaksCrossTraffic>> pulsed_cross_traffics_;
|
||||
std::vector<std::unique_ptr<FakeTcpCrossTraffic>> tcp_cross_traffics_;
|
||||
std::vector<std::unique_ptr<EndpointsContainer>> endpoints_containers_;
|
||||
std::vector<std::unique_ptr<EmulatedNetworkManager>> network_managers_;
|
||||
|
||||
|
Reference in New Issue
Block a user