tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 82601c6406a36b04ccea1b381f90d9ec7da01a2d
parent cc1d60c3604fcbe529f2c8d5e3c607dc854793cd
Author: Michael Froman <mfroman@mozilla.com>
Date:   Thu,  9 Oct 2025 13:30:09 -0500

Bug 1993083 - Vendor libwebrtc from 8d91ed7f24

Upstream commit: https://webrtc.googlesource.com/src/+/8d91ed7f24ad1138fb9cf663e19f7dac25680829
    Add DualPi2 NetworkQueue

    DualPi2NetworkQueue is a simplified version of the DualPi2 AQM controller in
    https://github.com/L4STeam/linux/. Concepts are described in
    https://datatracker.ietf.org/doc/html/rfc9332.

    Purpose is to be able to test WebRTC L4S implementation.

    Change-Id: I7381aabb7a2708253bd0f230ac1013677bbf5f5f

    Bug: webrtc:42225697
    Change-Id: I7381aabb7a2708253bd0f230ac1013677bbf5f5f
    Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/399900
    Commit-Queue: Per Kjellander <perkj@webrtc.org>
    Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
    Cr-Commit-Position: refs/heads/main@{#45139}

Diffstat:
Mthird_party/libwebrtc/README.mozilla.last-vendor | 4++--
Mthird_party/libwebrtc/api/test/network_emulation/BUILD.gn | 16+++++++++++++++-
Athird_party/libwebrtc/api/test/network_emulation/dual_pi2_network_queue.cc | 157+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Athird_party/libwebrtc/api/test/network_emulation/dual_pi2_network_queue.h | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Athird_party/libwebrtc/api/test/network_emulation/dual_pi2_network_queue_unittest.cc | 281+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mthird_party/libwebrtc/moz-patch-stack/s0095.patch | 2+-
Mthird_party/libwebrtc/test/peer_scenario/tests/BUILD.gn | 1+
Mthird_party/libwebrtc/test/peer_scenario/tests/l4s_test.cc | 56+++++++++++++++++++++++++++++++++++++++++++++++++++++---
8 files changed, 648 insertions(+), 7 deletions(-)

diff --git a/third_party/libwebrtc/README.mozilla.last-vendor b/third_party/libwebrtc/README.mozilla.last-vendor @@ -1,4 +1,4 @@ # ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /home/mfroman/mozilla/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc -libwebrtc updated from /home/mfroman/mozilla/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2025-10-09T18:28:09.753566+00:00. +libwebrtc updated from /home/mfroman/mozilla/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2025-10-09T18:29:58.547522+00:00. # base of lastest vendoring -5782b72f52 +8d91ed7f24 diff --git a/third_party/libwebrtc/api/test/network_emulation/BUILD.gn b/third_party/libwebrtc/api/test/network_emulation/BUILD.gn @@ -81,6 +81,8 @@ rtc_library("network_queue") { visibility = [ "*" ] sources = [ + "dual_pi2_network_queue.cc", + "dual_pi2_network_queue.h", "leaky_bucket_network_queue.cc", "leaky_bucket_network_queue.h", "network_queue.h", @@ -90,20 +92,32 @@ rtc_library("network_queue") { "../..:sequence_checker", "../..:simulated_network_api", "../../../rtc_base:checks", + "../../../rtc_base:logging", "../../../rtc_base:macromagic", + "../../../rtc_base:random", + "../../transport:ecn_marking", + "../../units:data_rate", + "../../units:data_size", + "../../units:time_delta", "../../units:timestamp", ] } rtc_library("network_queue_unittests") { - sources = [ "leaky_bucket_network_queue_unittest.cc" ] + sources = [ + "dual_pi2_network_queue_unittest.cc", + "leaky_bucket_network_queue_unittest.cc", + ] testonly = true deps = [ ":network_queue", "../..:simulated_network_api", "../../../test:test_support", + "../../transport:ecn_marking", + "../../units:data_rate", "../../units:data_size", + "../../units:time_delta", "../../units:timestamp", ] } diff --git a/third_party/libwebrtc/api/test/network_emulation/dual_pi2_network_queue.cc b/third_party/libwebrtc/api/test/network_emulation/dual_pi2_network_queue.cc @@ -0,0 +1,157 @@ +/* + * Copyright 2025 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "api/test/network_emulation/dual_pi2_network_queue.h" + +#include <algorithm> +#include <cstddef> +#include <optional> +#include <queue> + +#include "api/sequence_checker.h" +#include "api/test/simulated_network.h" +#include "api/transport/ecn_marking.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +DualPi2NetworkQueue::DualPi2NetworkQueue(const Config& config) + : config_(config), + step_threshold_(config.link_rate.IsInfinite() + ? DataSize::Infinity() + : config_.target_delay * config_.link_rate * 2), + random_(config.seed), + distribution_(0.0, 1.0) { + sequence_checker_.Detach(); +} + +void DualPi2NetworkQueue::SetMaxPacketCapacity(size_t max_packet_capacity) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + max_packet_capacity_ = max_packet_capacity; + // Hack to allow SetMaxpPacketCapactiy to be called before the queue is being + // used on another sequence. + sequence_checker_.Detach(); +} + +bool DualPi2NetworkQueue::EnqueuePacket(const PacketInFlightInfo& packet_info) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + UpdateBaseMarkingProbability(packet_info.send_time()); + if (max_packet_capacity_.has_value() && + l4s_queue_.size() + classic_queue_.size() >= *max_packet_capacity_) { + RTC_LOG(LS_WARNING) + << "DualPi2NetworkQueue::EnqueuePacket: Dropping packet " + "because max packet capacity is reached."; + return false; + } + + if (packet_info.ecn == EcnMarking::kNotEct || + packet_info.ecn == EcnMarking::kEct0) { + bool take_action = ShouldTakeAction(classic_drop_probability()); + if (!take_action) { + total_queued_size_ += packet_info.packet_size(); + classic_queue_.push(packet_info); + return true; + } + RTC_DLOG(LS_WARNING) + << "DualPi2NetworkQueue::EnqueuePacket: Dropping classic packet " + << packet_info.packet_id << ". Classic drop probability is " + << classic_drop_probability() + << " L4S queue size: " << l4s_queue_.size() + << " classic queue size: " << classic_queue_.size(); + + return false; + } + RTC_DCHECK(packet_info.ecn == EcnMarking::kEct1 || + packet_info.ecn == EcnMarking::kCe); + total_queued_size_ += packet_info.packet_size(); + bool take_action = ShouldTakeAction(l4s_marking_probability()); + if (take_action) { + PacketInFlightInfo ce_packet_info(packet_info); + ce_packet_info.ecn = EcnMarking::kCe; + l4s_queue_.push(ce_packet_info); + } else { + l4s_queue_.push(packet_info); + } + return true; +} + +std::optional<PacketInFlightInfo> DualPi2NetworkQueue::PeekNextPacket() const { + RTC_DCHECK_RUN_ON(&sequence_checker_); + if (!l4s_queue_.empty()) { + return l4s_queue_.front(); + } + if (!classic_queue_.empty()) { + return classic_queue_.front(); + } + return std::nullopt; +} + +std::optional<PacketInFlightInfo> DualPi2NetworkQueue::DequeuePacket( + Timestamp time_now) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + UpdateBaseMarkingProbability(time_now); + std::queue<PacketInFlightInfo>& queue = + l4s_queue_.empty() ? classic_queue_ : l4s_queue_; + if (queue.empty()) { + return std::nullopt; + } + + PacketInFlightInfo packet_info = queue.front(); + queue.pop(); + total_queued_size_ -= packet_info.packet_size(); + if (packet_info.ecn == EcnMarking::kEct1 && + ShouldTakeAction(l4s_marking_probability())) { + packet_info.ecn = EcnMarking::kCe; + } + return packet_info; +} + +bool DualPi2NetworkQueue::empty() const { + RTC_DCHECK_RUN_ON(&sequence_checker_); + return classic_queue_.empty() && l4s_queue_.empty(); +} + +void DualPi2NetworkQueue::UpdateBaseMarkingProbability(Timestamp time_now) { + if (time_now - config_.probability_update_interval < + last_probability_update_time_) { + return; + } + last_probability_update_time_ = time_now; + TimeDelta sojourn_time = + std::max(l4s_queue_delay(time_now), classic_queue_delay(time_now)); + TimeDelta proportional_update = + config_.alpha * (sojourn_time - config_.target_delay); + TimeDelta integral_update = + config_.beta * (sojourn_time - previous_sojourn_time_); + previous_sojourn_time_ = sojourn_time; + base_marking_probability_ += + proportional_update.seconds<double>() + integral_update.seconds<double>(); + + if (base_marking_probability_ < 0) { + base_marking_probability_ = 0; + } + if (base_marking_probability_ > 1.0) { + base_marking_probability_ = 1.0; + } + RTC_DLOG(LS_VERBOSE) << "base_marking_probability_: " + << base_marking_probability_; +} + +bool DualPi2NetworkQueue::ShouldTakeAction(double marking_probability) { + if (total_queued_size_ > step_threshold_) { + return true; + } + return distribution_(random_) < marking_probability; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/api/test/network_emulation/dual_pi2_network_queue.h b/third_party/libwebrtc/api/test/network_emulation/dual_pi2_network_queue.h @@ -0,0 +1,138 @@ +/* + * Copyright 2025 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef API_TEST_NETWORK_EMULATION_DUAL_PI2_NETWORK_QUEUE_H_ +#define API_TEST_NETWORK_EMULATION_DUAL_PI2_NETWORK_QUEUE_H_ + +#include <cstddef> +#include <memory> +#include <optional> +#include <queue> +#include <random> +#include <vector> + +#include "api/sequence_checker.h" +#include "api/test/network_emulation/network_queue.h" +#include "api/test/simulated_network.h" +#include "api/units/data_rate.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" + +namespace webrtc { + +// DualPi2NetworkQueue is a simplified version of the DualPi2 AQM controller in +// https://github.com/L4STeam/linux/. Concepts are described in +// https://datatracker.ietf.org/doc/html/rfc9332. +// Developed for testing purposes. +// Note that this implementation does not support the credit-based system +// (c_protection) from the real implementation and thus a L4S stream can +// completely starve a classic stream. +// +// TODO: bugs.webrtc.org/42225697 - Implement c_protection to better +// support testing of cross traffic with classic TCP. +class DualPi2NetworkQueue : public NetworkQueue { + public: + struct Config { + // Target delay for the queue. The queue will try to keep the delay of the + // L4S queue below this value. + TimeDelta target_delay = TimeDelta::Micros(500); + // Link rate puts a cap on how many bytes in total that can be stored in the + // queue and still approximately meet the target delay. The cap is + // calculated as: 2*target_delay * link_rate and applies to both queues + // combined. If more packets than this are enqueued, they will be CE marked + // (L4S) or dropped (classic). + DataRate link_rate = DataRate::PlusInfinity(); + + // These constants are used to calculate the proportional and integral + // factors when updating the marking probability. + // Values are from the original implementation. + double alpha = 0.16; + double beta = 3.2; + // Coupling factor. + int k = 2; + + // How often the base marking probability is updated. + TimeDelta probability_update_interval = TimeDelta::Millis(16); + int seed = 1; + }; + + DualPi2NetworkQueue() : DualPi2NetworkQueue(Config()) {} + explicit DualPi2NetworkQueue(const Config& config); + + void SetMaxPacketCapacity(size_t max_packet_capacity) override; + bool EnqueuePacket(const PacketInFlightInfo& packet_info) override; + + std::optional<PacketInFlightInfo> PeekNextPacket() const override; + std::optional<PacketInFlightInfo> DequeuePacket(Timestamp time_now) override; + std::vector<PacketInFlightInfo> DequeueDroppedPackets() override { + // DualPi2 always tail drop packets. + return {}; + } + bool empty() const override; + + // Returns the marking probability of the L4S the l4s queue. Public for + // testing. + double l4s_marking_probability() const { + return base_marking_probability_ * config_.k; + } + // Returns the drop probability of the classic queue. Public for + // testing. + double classic_drop_probability() const { + return (base_marking_probability_ * base_marking_probability_); + } + + private: + void UpdateBaseMarkingProbability(Timestamp time_now); + bool ShouldTakeAction(double marking_probability); + TimeDelta l4s_queue_delay(Timestamp time_now) const { + return l4s_queue_.empty() ? TimeDelta::Zero() + : time_now - l4s_queue_.front().send_time(); + } + + TimeDelta classic_queue_delay(Timestamp time_now) const { + return classic_queue_.empty() + ? TimeDelta::Zero() + : time_now - classic_queue_.front().send_time(); + } + + SequenceChecker sequence_checker_; + + const Config config_; + const DataSize step_threshold_; + + std::queue<PacketInFlightInfo> l4s_queue_; + std::queue<PacketInFlightInfo> classic_queue_; + + std::mt19937 random_; + std::uniform_real_distribution<double> distribution_; + + std::optional<size_t> max_packet_capacity_; + DataSize total_queued_size_; + double base_marking_probability_ = 0; + Timestamp last_probability_update_time_ = Timestamp::MinusInfinity(); + // The delay of the queue after the last probability update. + TimeDelta previous_sojourn_time_ = TimeDelta::Zero(); +}; + +class DualPi2NetworkQueueFactory : public NetworkQueueFactory { + public: + explicit DualPi2NetworkQueueFactory(const DualPi2NetworkQueue::Config& config) + : config_(config) {} + + std::unique_ptr<NetworkQueue> CreateQueue() override { + return std::make_unique<DualPi2NetworkQueue>(config_); + } + + private: + const DualPi2NetworkQueue::Config config_; +}; +} // namespace webrtc + +#endif // API_TEST_NETWORK_EMULATION_DUAL_PI2_NETWORK_QUEUE_H_ diff --git a/third_party/libwebrtc/api/test/network_emulation/dual_pi2_network_queue_unittest.cc b/third_party/libwebrtc/api/test/network_emulation/dual_pi2_network_queue_unittest.cc @@ -0,0 +1,281 @@ +/* + * Copyright 2025 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "api/test/network_emulation/dual_pi2_network_queue.h" + +#include <cstdint> +#include <limits> +#include <optional> + +#include "api/test/simulated_network.h" +#include "api/transport/ecn_marking.h" +#include "api/units/data_rate.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace { + +using ::testing::AnyOf; +using ::testing::Field; +using ::testing::Optional; +using ::testing::Property; + +const DataSize kPacketSize = DataSize::Bytes(1000); + +TEST(DualPi2NetworkQueueTest, EnqueuePacket) { + DualPi2NetworkQueue queue; + Timestamp send_time = Timestamp::Seconds(123); + PacketInFlightInfo packet_info(kPacketSize, send_time, /*packet_id=*/1, + EcnMarking::kNotEct); + EXPECT_TRUE(queue.EnqueuePacket(packet_info)); +} + +TEST(DualPi2NetworkQueueTest, PeekNextPacketReturnsNulloptWhenEmpty) { + DualPi2NetworkQueue queue; + EXPECT_EQ(queue.PeekNextPacket(), std::nullopt); +} + +TEST(DualPi2NetworkQueueTest, PeekNextPacketPrioritizeL4SQueue) { + DualPi2NetworkQueue queue; + Timestamp send_time = Timestamp::Seconds(123); + PacketInFlightInfo packet_info_classic(kPacketSize, send_time, + /*packet_id=*/1, EcnMarking::kNotEct); + queue.EnqueuePacket(packet_info_classic); + PacketInFlightInfo packet_info_l4s_1(kPacketSize, send_time, + /*packet_id=*/2, EcnMarking::kEct1); + queue.EnqueuePacket(packet_info_l4s_1); + PacketInFlightInfo packet_info_l4s_2(kPacketSize, send_time, + /*packet_id=*/3, EcnMarking::kEct1); + queue.EnqueuePacket(packet_info_l4s_2); + std::optional<PacketInFlightInfo> peeked_packet = queue.PeekNextPacket(); + ASSERT_TRUE(peeked_packet.has_value()); + EXPECT_EQ(peeked_packet.value().packet_id, 2u); +} + +TEST(DualPi2NetworkQueueTest, DequeuePacketReturnsNulloptWhenEmpty) { + DualPi2NetworkQueue queue; + EXPECT_EQ(queue.DequeuePacket(Timestamp::Seconds(123)), std::nullopt); +} + +TEST(DualPi2NetworkQueueTest, DequeuePacketPrioritizeL4SQueue) { + DualPi2NetworkQueue queue; + Timestamp send_time = Timestamp::Seconds(123); + PacketInFlightInfo packet_info_classic(kPacketSize, send_time, + /*packet_id=*/1, EcnMarking::kNotEct); + queue.EnqueuePacket(packet_info_classic); + PacketInFlightInfo packet_info_l4s_1(kPacketSize, send_time, + /*packet_id=*/2, EcnMarking::kEct1); + queue.EnqueuePacket(packet_info_l4s_1); + PacketInFlightInfo packet_info_l4s_2(kPacketSize, send_time, + /*packet_id=*/3, EcnMarking::kEct1); + queue.EnqueuePacket(packet_info_l4s_2); + Timestamp dequeue_time = Timestamp::Seconds(123); + EXPECT_THAT( + queue.DequeuePacket(dequeue_time), + Optional(AllOf(Field(&PacketInFlightInfo::packet_id, 2), + Field(&PacketInFlightInfo::ecn, EcnMarking::kEct1), + Property(&PacketInFlightInfo::send_time, send_time)))); + EXPECT_THAT( + queue.DequeuePacket(dequeue_time), + Optional(AllOf(Field(&PacketInFlightInfo::packet_id, 3), + Field(&PacketInFlightInfo::ecn, EcnMarking::kEct1), + Property(&PacketInFlightInfo::send_time, send_time)))); + EXPECT_THAT( + queue.DequeuePacket(dequeue_time), + Optional(AllOf(Field(&PacketInFlightInfo::packet_id, 1), + Field(&PacketInFlightInfo::ecn, EcnMarking::kNotEct), + Property(&PacketInFlightInfo::send_time, send_time)))); +} + +TEST(DualPi2NetworkQueueTest, + CeMarkingProbabilityIncreaseIfSojournTimeTooHigh) { + DualPi2NetworkQueue queue; + + double marking_probability = 0; + Timestamp now = Timestamp::Seconds(123); + + for (int i = 0; i < 4; ++i) { + queue.EnqueuePacket(PacketInFlightInfo(kPacketSize, now, + /*packet_id=*/i, EcnMarking::kEct1)); + // Dequeue 1 packet after 17ms, 1ms more than the probability update + // interval and more than the target delay. + now += TimeDelta::Millis(17); + ASSERT_THAT(queue.DequeuePacket(now), + Optional(Field(&PacketInFlightInfo::packet_id, i))); + EXPECT_GT(queue.l4s_marking_probability(), marking_probability); + marking_probability = queue.l4s_marking_probability(); + EXPECT_GT(marking_probability, 0); + EXPECT_LE(marking_probability, std::numeric_limits<uint32_t>::max()); + } +} + +TEST(DualPi2NetworkQueueTest, + CeMarkingProbabilityIncreaseIfSojournTimeTooHighForClassicTraffic) { + DualPi2NetworkQueue queue; + + double marking_probability = 0; + Timestamp now = Timestamp::Seconds(123); + + for (int i = 0; i < 4; ++i) { + queue.EnqueuePacket(PacketInFlightInfo(kPacketSize, now, + /*packet_id=*/i, EcnMarking::kEct0)); + // Dequeue 1 packet after 17ms, 1ms more than the probability update + // interval and more than the target delay. + now += TimeDelta::Millis(17); + ASSERT_THAT(queue.DequeuePacket(now), + Optional(Field(&PacketInFlightInfo::packet_id, i))); + EXPECT_GT(queue.l4s_marking_probability(), marking_probability); + marking_probability = queue.l4s_marking_probability(); + EXPECT_GT(marking_probability, 0); + EXPECT_LE(marking_probability, std::numeric_limits<uint32_t>::max()); + } +} + +TEST(DualPi2NetworkQueueTest, + CeMarkingProbabilityDontIncreaseIfSojournTimeEqualToTarget) { + DualPi2NetworkQueue queue; + Timestamp now = Timestamp::Seconds(123); + int i = 0; + double marking_probability_at_equilibrium = -1; + while (now < Timestamp::Seconds(123 + 1)) { + i = i + 2; + queue.EnqueuePacket(PacketInFlightInfo(kPacketSize, now, + /*packet_id=*/i, EcnMarking::kEct1)); + now += TimeDelta::Micros(500); + queue.EnqueuePacket(PacketInFlightInfo(kPacketSize, now, + /*packet_id=*/i + 1, + EcnMarking::kEct1)); + + ASSERT_THAT(queue.DequeuePacket(now), + Optional(Field(&PacketInFlightInfo::packet_id, i))); + now += TimeDelta::Micros(500); + ASSERT_THAT(queue.DequeuePacket(now), + Optional(Field(&PacketInFlightInfo::packet_id, i + 1))); + if (queue.l4s_marking_probability() != 0 && + marking_probability_at_equilibrium == -1) { + // Both proportional and integral updates are zero after the second update + // since the sojourn time is equal to the target delay. + marking_probability_at_equilibrium = queue.l4s_marking_probability(); + } + } + EXPECT_EQ(queue.l4s_marking_probability(), + marking_probability_at_equilibrium); +} + +TEST(DualPi2NetworkQueueTest, L4SQueueCeMarkIfDelayIsTooHigh) { + DualPi2NetworkQueue queue; + bool has_seen_ce_marked_packet = false; + Timestamp now = Timestamp::Seconds(123); + int i = 0; + while (now < Timestamp::Seconds(123 + 1)) { + now += TimeDelta::Millis(20); + // Enqueue 2 L4S packets but only dequeue one. Delay will grow.... + queue.EnqueuePacket(PacketInFlightInfo(kPacketSize, now, + /*packet_id=*/i++, + EcnMarking::kEct1)); + queue.EnqueuePacket(PacketInFlightInfo(kPacketSize, now, + /*packet_id=*/i++, + EcnMarking::kEct1)); + + std::optional<PacketInFlightInfo> dequeued_packet = + queue.DequeuePacket(now); + ASSERT_TRUE(dequeued_packet.has_value()); + if (dequeued_packet->ecn == EcnMarking::kCe) { + EXPECT_GT(queue.l4s_marking_probability(), 0); + has_seen_ce_marked_packet = true; + break; + } + } + EXPECT_TRUE(has_seen_ce_marked_packet); +} + +TEST(DualPi2NetworkQueueTest, ClassicQueueDropPacketIfL4SDelayIsTooHigh) { + DualPi2NetworkQueue queue; + bool has_dropped_classic_packet = false; + Timestamp now = Timestamp::Seconds(123); + int i = 0; + while (now < Timestamp::Seconds(123 + 1)) { + now += TimeDelta::Millis(20); + // Enqueue 2 L4S packets but only dequeue one. L4S delay will grow.... + queue.EnqueuePacket(PacketInFlightInfo(kPacketSize, now, + /*packet_id=*/i++, + EcnMarking::kEct1)); + queue.EnqueuePacket(PacketInFlightInfo(kPacketSize, now, + /*packet_id=*/i++, + EcnMarking::kEct1)); + // Enqueue a classic packet. + has_dropped_classic_packet |= queue.EnqueuePacket( + PacketInFlightInfo(kPacketSize, now, + /*packet_id=*/i++, EcnMarking::kEct0)); + + std::optional<PacketInFlightInfo> dequeued_packet = + queue.DequeuePacket(now); + ASSERT_TRUE(dequeued_packet.has_value()); + // Dequeued packets are always L4S. + EXPECT_THAT(dequeued_packet->ecn, + AnyOf(EcnMarking::kEct1, EcnMarking::kCe)); + } + EXPECT_TRUE(has_dropped_classic_packet); +} + +TEST(DualPi2NetworkQueueTest, CeMarksIfStepThresholdIsReached) { + DualPi2NetworkQueue::Config config; + config.link_rate = DataRate::KilobitsPerSec(100); + const DataSize kStepThreshold = config.target_delay * config.link_rate * 2; + DualPi2NetworkQueue queue(config); + DataSize total_queued_size = DataSize::Zero(); + Timestamp now = Timestamp::Seconds(123); + + int i = 0; + while (total_queued_size < kStepThreshold) { + ASSERT_TRUE(queue.EnqueuePacket(PacketInFlightInfo(kPacketSize, now, + /*packet_id=*/i++, + EcnMarking::kEct1))); + total_queued_size += kPacketSize; + } + std::optional<PacketInFlightInfo> dequeued_packet = queue.DequeuePacket(now); + ASSERT_TRUE(dequeued_packet.has_value()); + EXPECT_EQ(dequeued_packet->ecn, EcnMarking::kCe); +} + +TEST(DualPi2NetworkQueueTest, DropsClassicPacketIfStepThresholdIsReached) { + DualPi2NetworkQueue::Config config; + config.link_rate = DataRate::KilobitsPerSec(100); + const DataSize kStepThreshold = config.target_delay * config.link_rate * 2; + DualPi2NetworkQueue queue(config); + DataSize total_queued_size = DataSize::Zero(); + Timestamp now = Timestamp::Seconds(123); + int i = 0; + + while (total_queued_size < kStepThreshold) { + ASSERT_TRUE(queue.EnqueuePacket(PacketInFlightInfo(kPacketSize, now, + /*packet_id=*/i++, + EcnMarking::kEct1))); + total_queued_size += kPacketSize; + } + + EXPECT_FALSE(queue.EnqueuePacket(PacketInFlightInfo(kPacketSize, now, + /*packet_id=*/i++, + EcnMarking::kEct0))); + + while (total_queued_size < kStepThreshold) { + ASSERT_TRUE(queue.EnqueuePacket(PacketInFlightInfo(kPacketSize, now, + /*packet_id=*/i++, + EcnMarking::kEct1))); + total_queued_size += kPacketSize; + } +} + +} // namespace +} // namespace webrtc diff --git a/third_party/libwebrtc/moz-patch-stack/s0095.patch b/third_party/libwebrtc/moz-patch-stack/s0095.patch @@ -9,7 +9,7 @@ Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/6baf67202c67b27c6 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/test/network_emulation/BUILD.gn b/api/test/network_emulation/BUILD.gn -index de58887b07..665af721e3 100644 +index 681b289c6a..094c962f67 100644 --- a/api/test/network_emulation/BUILD.gn +++ b/api/test/network_emulation/BUILD.gn @@ -6,10 +6,10 @@ diff --git a/third_party/libwebrtc/test/peer_scenario/tests/BUILD.gn b/third_party/libwebrtc/test/peer_scenario/tests/BUILD.gn @@ -30,6 +30,7 @@ if (rtc_include_tests) { "../../../api:rtp_transceiver_direction", "../../../api:scoped_refptr", "../../../api/test/network_emulation", + "../../../api/test/network_emulation:network_queue", "../../../api/transport:ecn_marking", "../../../api/units:data_rate", "../../../api/units:time_delta", diff --git a/third_party/libwebrtc/test/peer_scenario/tests/l4s_test.cc b/third_party/libwebrtc/test/peer_scenario/tests/l4s_test.cc @@ -17,6 +17,7 @@ #include "api/scoped_refptr.h" #include "api/stats/rtc_stats_report.h" #include "api/stats/rtcstats_objects.h" +#include "api/test/network_emulation/dual_pi2_network_queue.h" #include "api/test/network_emulation/network_emulation_interfaces.h" #include "api/transport/ecn_marking.h" #include "api/units/data_rate.h" @@ -195,7 +196,7 @@ TEST(L4STest, NegotiateAndUseCcfbIfEnabled) { EXPECT_EQ(ret_node_feedback_counter.FeedbackAccordingToTransportCc(), 0); } -TEST(L4STest, CallerAdaptToLinkCapacityWithoutEcn) { +TEST(L4STest, CallerAdaptToLinkCapacityOnNetworkWithoutEcn) { PeerScenario s(*test_info_); PeerScenarioClient::Config config; @@ -217,7 +218,56 @@ TEST(L4STest, CallerAdaptToLinkCapacityWithoutEcn) { auto signaling = s.ConnectSignaling(caller, callee, {caller_to_callee}, {callee_to_caller}); PeerScenarioClient::VideoSendTrackConfig video_conf; - video_conf.generator.squares_video->framerate = 15; + video_conf.generator.squares_video->framerate = 30; + video_conf.generator.squares_video->width = 640; + video_conf.generator.squares_video->height = 360; + caller->CreateVideo("VIDEO_1", video_conf); + + signaling.StartIceSignaling(); + std::atomic<bool> offer_exchange_done(false); + signaling.NegotiateSdp([&](const SessionDescriptionInterface& answer) { + offer_exchange_done = true; + }); + s.WaitAndProcess(&offer_exchange_done); + s.ProcessMessages(TimeDelta::Seconds(3)); + DataRate available_bwe = + GetAvailableSendBitrate(GetStatsAndProcess(s, caller)); + EXPECT_GT(available_bwe.kbps(), 450); + EXPECT_LT(available_bwe.kbps(), 610); +} + +// Note - this test only test that the +// caller adapt to the link capacity. It does not test that the caller uses ECN +// to adapt even though the network can mark packets with CE. +// TODO: bugs.webrtc.org/42225697 - actually test that the caller adapt to ECN +// marking. +TEST(L4STest, CallerAdaptToLinkCapacityOnNetworkWithEcn) { + PeerScenario s(*test_info_); + PeerScenarioClient::Config config; + config.field_trials.Set("WebRTC-RFC8888CongestionControlFeedback", "Enabled"); + + PeerScenarioClient* caller = s.CreateClient(config); + PeerScenarioClient* callee = s.CreateClient(config); + + DualPi2NetworkQueueFactory dual_pi_factory({}); + auto caller_to_callee = s.net() + ->NodeBuilder() + .queue_factory(dual_pi_factory) + .capacity(DataRate::KilobitsPerSec(600)) + .Build() + .node; + auto callee_to_caller = s.net()->NodeBuilder().Build().node; + s.net()->CreateRoute(caller->endpoint(), {caller_to_callee}, + callee->endpoint()); + s.net()->CreateRoute(callee->endpoint(), {callee_to_caller}, + caller->endpoint()); + + auto signaling = s.ConnectSignaling(caller, callee, {caller_to_callee}, + {callee_to_caller}); + PeerScenarioClient::VideoSendTrackConfig video_conf; + video_conf.generator.squares_video->framerate = 30; + video_conf.generator.squares_video->width = 640; + video_conf.generator.squares_video->height = 360; caller->CreateVideo("VIDEO_1", video_conf); signaling.StartIceSignaling(); @@ -229,7 +279,7 @@ TEST(L4STest, CallerAdaptToLinkCapacityWithoutEcn) { s.ProcessMessages(TimeDelta::Seconds(3)); DataRate available_bwe = GetAvailableSendBitrate(GetStatsAndProcess(s, caller)); - EXPECT_GT(available_bwe.kbps(), 500); + EXPECT_GT(available_bwe.kbps(), 450); EXPECT_LT(available_bwe.kbps(), 610); }