|  | /* | 
|  | *  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_ |