blob: bd2e3815884d29377c2d21721a290575fd0bad5e [file] [log] [blame]
/*
* 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_