gfe-relnote: In QUIC bandwidth sampler, only remove packets when RemoveObsoletePackets is called. Protected by --gfe2_reloadable_flag_quic_bw_sampler_remove_packets_once_per_congestion_event.
PiperOrigin-RevId: 285110271
Change-Id: I27567839a190136c03fef54a66261ab34d8c1f82
diff --git a/quic/core/congestion_control/bandwidth_sampler.cc b/quic/core/congestion_control/bandwidth_sampler.cc
index cc90cb0..a4cdc33 100644
--- a/quic/core/congestion_control/bandwidth_sampler.cc
+++ b/quic/core/congestion_control/bandwidth_sampler.cc
@@ -64,7 +64,12 @@
max_tracked_packets_(GetQuicFlag(FLAGS_quic_max_tracked_packet_count)),
unacked_packet_map_(unacked_packet_map),
max_ack_height_tracker_(max_height_tracker_window_length),
- total_bytes_acked_after_last_ack_event_(0) {}
+ total_bytes_acked_after_last_ack_event_(0) {
+ if (remove_packets_once_per_congestion_event_) {
+ QUIC_RELOADABLE_FLAG_COUNT(
+ quic_bw_sampler_remove_packets_once_per_congestion_event);
+ }
+}
BandwidthSampler::~BandwidthSampler() {}
@@ -115,8 +120,8 @@
}
}
- bool success =
- connection_state_map_.Emplace(packet_number, sent_time, bytes, *this);
+ bool success = connection_state_map_.Emplace(packet_number, sent_time, bytes,
+ bytes_in_flight, *this);
QUIC_BUG_IF(!success) << "BandwidthSampler failed to insert the packet "
"into the map, most likely because it's already "
"in it.";
@@ -149,7 +154,9 @@
}
BandwidthSample sample =
OnPacketAcknowledgedInner(ack_time, packet_number, *sent_packet_pointer);
- connection_state_map_.Remove(packet_number);
+ if (!remove_packets_once_per_congestion_event_) {
+ connection_state_map_.Remove(packet_number);
+ }
return sample;
}
@@ -220,16 +227,27 @@
return sample;
}
-SendTimeState BandwidthSampler::OnPacketLost(QuicPacketNumber packet_number) {
+SendTimeState BandwidthSampler::OnPacketLost(QuicPacketNumber packet_number,
+ QuicPacketLength bytes_lost) {
// TODO(vasilvv): see the comment for the case of missing packets in
// BandwidthSampler::OnPacketAcknowledged on why this does not raise a
// QUIC_BUG when removal fails.
SendTimeState send_time_state;
- send_time_state.is_valid = connection_state_map_.Remove(
- packet_number, [&](const ConnectionStateOnSentPacket& sent_packet) {
- total_bytes_lost_ += sent_packet.size;
- SentPacketToSendTimeState(sent_packet, &send_time_state);
- });
+
+ if (remove_packets_once_per_congestion_event_) {
+ total_bytes_lost_ += bytes_lost;
+ ConnectionStateOnSentPacket* sent_packet_pointer =
+ connection_state_map_.GetEntry(packet_number);
+ if (sent_packet_pointer != nullptr) {
+ SentPacketToSendTimeState(*sent_packet_pointer, &send_time_state);
+ }
+ } else {
+ send_time_state.is_valid = connection_state_map_.Remove(
+ packet_number, [&](const ConnectionStateOnSentPacket& sent_packet) {
+ total_bytes_lost_ += sent_packet.size;
+ SentPacketToSendTimeState(sent_packet, &send_time_state);
+ });
+ }
return send_time_state;
}
@@ -251,6 +269,10 @@
// QuicSentPacketManager::RetransmitCryptoPackets retransmits a crypto packet,
// the packet is removed from QuicUnackedPacketMap's inflight, but is not
// marked as acked or lost in the BandwidthSampler.
+ if (remove_packets_once_per_congestion_event_) {
+ connection_state_map_.RemoveUpTo(least_unacked);
+ return;
+ }
while (!connection_state_map_.IsEmpty() &&
connection_state_map_.first_packet() < least_unacked) {
connection_state_map_.Remove(
diff --git a/quic/core/congestion_control/bandwidth_sampler.h b/quic/core/congestion_control/bandwidth_sampler.h
index 51f5e87..c7c9862 100644
--- a/quic/core/congestion_control/bandwidth_sampler.h
+++ b/quic/core/congestion_control/bandwidth_sampler.h
@@ -29,17 +29,20 @@
is_app_limited(false),
total_bytes_sent(0),
total_bytes_acked(0),
- total_bytes_lost(0) {}
+ total_bytes_lost(0),
+ bytes_in_flight(0) {}
SendTimeState(bool is_app_limited,
QuicByteCount total_bytes_sent,
QuicByteCount total_bytes_acked,
- QuicByteCount total_bytes_lost)
+ QuicByteCount total_bytes_lost,
+ QuicByteCount bytes_in_flight)
: is_valid(true),
is_app_limited(is_app_limited),
total_bytes_sent(total_bytes_sent),
total_bytes_acked(total_bytes_acked),
- total_bytes_lost(total_bytes_lost) {}
+ total_bytes_lost(total_bytes_lost),
+ bytes_in_flight(bytes_in_flight) {}
SendTimeState(const SendTimeState& other) = default;
@@ -60,6 +63,11 @@
// Total number of lost bytes at the time the packet was sent.
QuicByteCount total_bytes_lost;
+
+ // Total number of inflight bytes right before the time the packet was sent.
+ // It should be equal to |total_bytes_sent| minus the sum of
+ // |total_bytes_acked|, |total_bytes_lost| and total neutered bytes.
+ QuicByteCount bytes_in_flight;
};
struct QUIC_EXPORT_PRIVATE BandwidthSample {
@@ -148,7 +156,8 @@
// Informs the sampler that a packet is considered lost and it should no
// longer keep track of it.
- virtual SendTimeState OnPacketLost(QuicPacketNumber packet_number) = 0;
+ virtual SendTimeState OnPacketLost(QuicPacketNumber packet_number,
+ QuicPacketLength bytes_lost) = 0;
// Informs the sampler that the connection is currently app-limited, causing
// the sampler to enter the app-limited phase. The phase will expire by
@@ -264,7 +273,8 @@
QuicPacketNumber packet_number) override;
QuicByteCount OnAckEventEnd(QuicBandwidth bandwidth_estimate,
QuicRoundTripCount round_trip_count);
- SendTimeState OnPacketLost(QuicPacketNumber packet_number) override;
+ SendTimeState OnPacketLost(QuicPacketNumber packet_number,
+ QuicPacketLength bytes_lost) override;
void OnAppLimited() override;
@@ -293,6 +303,10 @@
max_ack_height_tracker_.Reset(new_height, new_time);
}
+ bool remove_packets_once_per_congestion_event() const {
+ return remove_packets_once_per_congestion_event_;
+ }
+
private:
friend class test::BandwidthSamplerPeer;
@@ -325,8 +339,10 @@
// Snapshot constructor. Records the current state of the bandwidth
// sampler.
+ // |prior_bytes_in_flight| is the bytes in flight before the packet is sent.
ConnectionStateOnSentPacket(QuicTime sent_time,
QuicByteCount size,
+ QuicByteCount prior_bytes_in_flight,
const BandwidthSampler& sampler)
: sent_time(sent_time),
size(size),
@@ -337,7 +353,10 @@
send_time_state(sampler.is_app_limited_,
sampler.total_bytes_sent_,
sampler.total_bytes_acked_,
- sampler.total_bytes_lost_) {}
+ sampler.total_bytes_lost_,
+ sampler.remove_packets_once_per_congestion_event()
+ ? prior_bytes_in_flight
+ : 0) {}
// Default constructor. Required to put this structure into
// PacketNumberIndexedQueue.
@@ -406,6 +425,10 @@
MaxAckHeightTracker max_ack_height_tracker_;
QuicByteCount total_bytes_acked_after_last_ack_event_;
+
+ // Latched value of quic_bw_sampler_remove_packets_once_per_congestion_event.
+ const bool remove_packets_once_per_congestion_event_ = GetQuicReloadableFlag(
+ quic_bw_sampler_remove_packets_once_per_congestion_event);
};
} // namespace quic
diff --git a/quic/core/congestion_control/bandwidth_sampler_test.cc b/quic/core/congestion_control/bandwidth_sampler_test.cc
index 59318a9..a2b858f 100644
--- a/quic/core/congestion_control/bandwidth_sampler_test.cc
+++ b/quic/core/congestion_control/bandwidth_sampler_test.cc
@@ -85,7 +85,7 @@
sampler_, QuicPacketNumber(packet_number));
bytes_in_flight_ -= size;
SendTimeState send_time_state =
- sampler_.OnPacketLost(QuicPacketNumber(packet_number));
+ sampler_.OnPacketLost(QuicPacketNumber(packet_number), size);
EXPECT_TRUE(send_time_state.is_valid);
return send_time_state;
}
@@ -133,6 +133,9 @@
QuicBandwidth current_sample = AckPacket(i);
EXPECT_EQ(expected_bandwidth, current_sample);
}
+ if (sampler_.remove_packets_once_per_congestion_event()) {
+ sampler_.RemoveObsoletePackets(QuicPacketNumber(25));
+ }
EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
EXPECT_EQ(0u, bytes_in_flight_);
}
@@ -211,6 +214,9 @@
EXPECT_EQ(expected_bandwidth, last_bandwidth);
clock_.AdvanceTime(time_between_packets);
}
+ if (sampler_.remove_packets_once_per_congestion_event()) {
+ sampler_.RemoveObsoletePackets(QuicPacketNumber(41));
+ }
EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
EXPECT_EQ(0u, bytes_in_flight_);
}
@@ -251,6 +257,9 @@
}
clock_.AdvanceTime(time_between_packets);
}
+ if (sampler_.remove_packets_once_per_congestion_event()) {
+ sampler_.RemoveObsoletePackets(QuicPacketNumber(41));
+ }
EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
EXPECT_EQ(0u, bytes_in_flight_);
}
@@ -299,6 +308,9 @@
clock_.AdvanceTime(time_between_packets);
}
+ if (sampler_.remove_packets_once_per_congestion_event()) {
+ sampler_.RemoveObsoletePackets(QuicPacketNumber(41));
+ }
// Since only congestion controlled packets are entered into the map, it has
// to be empty at this point.
EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
@@ -327,6 +339,9 @@
clock_.AdvanceTime(ridiculously_small_time_delta);
}
EXPECT_EQ(expected_bandwidth, last_bandwidth);
+ if (sampler_.remove_packets_once_per_congestion_event()) {
+ sampler_.RemoveObsoletePackets(QuicPacketNumber(41));
+ }
EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
EXPECT_EQ(0u, bytes_in_flight_);
}
@@ -356,6 +371,9 @@
EXPECT_EQ(expected_bandwidth, last_bandwidth);
clock_.AdvanceTime(time_between_packets);
}
+ if (sampler_.remove_packets_once_per_congestion_event()) {
+ sampler_.RemoveObsoletePackets(QuicPacketNumber(61));
+ }
EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
EXPECT_EQ(0u, bytes_in_flight_);
}
@@ -406,6 +424,9 @@
clock_.AdvanceTime(time_between_packets);
}
+ if (sampler_.remove_packets_once_per_congestion_event()) {
+ sampler_.RemoveObsoletePackets(QuicPacketNumber(81));
+ }
EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
EXPECT_EQ(0u, bytes_in_flight_);
}
@@ -458,9 +479,15 @@
EXPECT_EQ(5u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
sampler_.RemoveObsoletePackets(QuicPacketNumber(4));
EXPECT_EQ(2u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
- sampler_.OnPacketLost(QuicPacketNumber(4));
+ sampler_.OnPacketLost(QuicPacketNumber(4), kRegularPacketSize);
+ if (sampler_.remove_packets_once_per_congestion_event()) {
+ sampler_.RemoveObsoletePackets(QuicPacketNumber(5));
+ }
EXPECT_EQ(1u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
AckPacket(5);
+ if (sampler_.remove_packets_once_per_congestion_event()) {
+ sampler_.RemoveObsoletePackets(QuicPacketNumber(6));
+ }
EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
}
diff --git a/quic/core/congestion_control/bbr2_misc.cc b/quic/core/congestion_control/bbr2_misc.cc
index a527417..8b8e663 100644
--- a/quic/core/congestion_control/bbr2_misc.cc
+++ b/quic/core/congestion_control/bbr2_misc.cc
@@ -156,18 +156,35 @@
}
for (const LostPacket& packet : lost_packets) {
- const SendTimeState send_time_state =
- bandwidth_sampler_.OnPacketLost(packet.packet_number);
+ const SendTimeState send_time_state = bandwidth_sampler_.OnPacketLost(
+ packet.packet_number, packet.bytes_lost);
if (send_time_state.is_valid) {
congestion_event->last_lost_sample = {packet.packet_number,
send_time_state};
}
}
- congestion_event->bytes_in_flight = bytes_in_flight();
+ if (!bandwidth_sampler_.remove_packets_once_per_congestion_event()) {
+ congestion_event->bytes_in_flight = bytes_in_flight();
+ }
congestion_event->bytes_acked = total_bytes_acked() - prior_bytes_acked;
congestion_event->bytes_lost = total_bytes_lost() - prior_bytes_lost;
+ if (bandwidth_sampler_.remove_packets_once_per_congestion_event()) {
+ if (congestion_event->prior_bytes_in_flight >=
+ congestion_event->bytes_acked + congestion_event->bytes_lost) {
+ congestion_event->bytes_in_flight =
+ congestion_event->prior_bytes_in_flight -
+ congestion_event->bytes_acked - congestion_event->bytes_lost;
+ } else {
+ QUIC_LOG_FIRST_N(ERROR, 1)
+ << "prior_bytes_in_flight:" << congestion_event->prior_bytes_in_flight
+ << " is smaller than the sum of bytes_acked:"
+ << congestion_event->bytes_acked
+ << " and bytes_lost:" << congestion_event->bytes_lost;
+ congestion_event->bytes_in_flight = 0;
+ }
+ }
bytes_lost_in_round_ += congestion_event->bytes_lost;
bandwidth_sampler_.OnAckEventEnd(BandwidthEstimate(), RoundTripCount());
diff --git a/quic/core/congestion_control/bbr2_misc.h b/quic/core/congestion_control/bbr2_misc.h
index 8f08a58..760f4c9 100644
--- a/quic/core/congestion_control/bbr2_misc.h
+++ b/quic/core/congestion_control/bbr2_misc.h
@@ -228,6 +228,9 @@
// The congestion window prior to the processing of the ack/loss events.
QuicByteCount prior_cwnd;
+ // Total bytes inflight before the processing of the ack/loss events.
+ QuicByteCount prior_bytes_in_flight = 0;
+
// Total bytes inflight after the processing of the ack/loss events.
QuicByteCount bytes_in_flight = 0;
@@ -367,6 +370,7 @@
}
QuicByteCount bytes_in_flight() const {
+ DCHECK(!bandwidth_sampler_.remove_packets_once_per_congestion_event());
return total_bytes_sent() - total_bytes_acked() - total_bytes_lost();
}
@@ -500,6 +504,9 @@
QUIC_EXPORT_PRIVATE inline QuicByteCount BytesInFlight(
const SendTimeState& send_state) {
DCHECK(send_state.is_valid);
+ if (send_state.bytes_in_flight != 0) {
+ return send_state.bytes_in_flight;
+ }
return send_state.total_bytes_sent - send_state.total_bytes_acked -
send_state.total_bytes_lost;
}
diff --git a/quic/core/congestion_control/bbr2_sender.cc b/quic/core/congestion_control/bbr2_sender.cc
index 31bf299..3e707f3 100644
--- a/quic/core/congestion_control/bbr2_sender.cc
+++ b/quic/core/congestion_control/bbr2_sender.cc
@@ -150,6 +150,7 @@
<< " prior_cwnd:" << cwnd_ << " @ " << event_time;
Bbr2CongestionEvent congestion_event;
congestion_event.prior_cwnd = cwnd_;
+ congestion_event.prior_bytes_in_flight = prior_in_flight;
congestion_event.is_probing_for_bandwidth =
BBR2_MODE_DISPATCH(IsProbingForBandwidth());
@@ -193,7 +194,7 @@
<< this << " END CongestionEvent(acked:" << acked_packets
<< ", lost:" << lost_packets.size() << ") "
<< ", Mode:" << mode_ << ", RttCount:" << model_.RoundTripCount()
- << ", BytesInFlight:" << model_.bytes_in_flight()
+ << ", BytesInFlight:" << congestion_event.bytes_in_flight
<< ", PacingRate:" << PacingRate(0) << ", CWND:" << GetCongestionWindow()
<< ", PacingGain:" << model_.pacing_gain()
<< ", CwndGain:" << model_.cwnd_gain()
@@ -274,7 +275,7 @@
HasRetransmittableData is_retransmittable) {
QUIC_DVLOG(3) << this << " OnPacketSent: pkn:" << packet_number
<< ", bytes:" << bytes << ", cwnd:" << cwnd_
- << ", inflight:" << model_.bytes_in_flight() + bytes
+ << ", inflight:" << bytes_in_flight + bytes
<< ", total_sent:" << model_.total_bytes_sent() + bytes
<< ", total_acked:" << model_.total_bytes_acked()
<< ", total_lost:" << model_.total_bytes_lost() << " @ "
@@ -327,7 +328,7 @@
if (flexible_app_limited_) {
const bool is_pipe_sufficiently_full = IsPipeSufficientlyFull();
QUIC_DVLOG(3) << this << " CWND: " << GetCongestionWindow()
- << ", inflight: " << model_.bytes_in_flight()
+ << ", inflight: " << unacked_packets_->bytes_in_flight()
<< ", pacing_rate: " << PacingRate(0)
<< ", flexible_app_limited_: true, ShouldSendProbingPacket: "
<< !is_pipe_sufficiently_full;
@@ -338,20 +339,20 @@
}
bool Bbr2Sender::IsPipeSufficientlyFull() const {
+ QuicByteCount bytes_in_flight = unacked_packets_->bytes_in_flight();
// See if we need more bytes in flight to see more bandwidth.
if (mode_ == Bbr2Mode::STARTUP) {
// STARTUP exits if it doesn't observe a 25% bandwidth increase, so the CWND
// must be more than 25% above the target.
- return model_.bytes_in_flight() >= GetTargetCongestionWindow(1.5);
+ return bytes_in_flight >= GetTargetCongestionWindow(1.5);
}
if (model_.pacing_gain() > 1) {
// Super-unity PROBE_BW doesn't exit until 1.25 * BDP is achieved.
- return model_.bytes_in_flight() >=
- GetTargetCongestionWindow(model_.pacing_gain());
+ return bytes_in_flight >= GetTargetCongestionWindow(model_.pacing_gain());
}
// If bytes_in_flight are above the target congestion window, it should be
// possible to observe the same or more bandwidth if it's available.
- return model_.bytes_in_flight() >= GetTargetCongestionWindow(1.1);
+ return bytes_in_flight >= GetTargetCongestionWindow(1.1);
}
std::string Bbr2Sender::GetDebugState() const {
diff --git a/quic/core/congestion_control/bbr_sender.cc b/quic/core/congestion_control/bbr_sender.cc
index 8441455..4973744 100644
--- a/quic/core/congestion_control/bbr_sender.cc
+++ b/quic/core/congestion_control/bbr_sender.cc
@@ -495,7 +495,7 @@
void BbrSender::DiscardLostPackets(const LostPacketVector& lost_packets) {
for (const LostPacket& packet : lost_packets) {
- sampler_.OnPacketLost(packet.packet_number);
+ sampler_.OnPacketLost(packet.packet_number, packet.bytes_lost);
if (mode_ == STARTUP) {
if (stats_) {
++stats_->slowstart_packets_lost;
diff --git a/quic/core/packet_number_indexed_queue.h b/quic/core/packet_number_indexed_queue.h
index 470a58c..269858f 100644
--- a/quic/core/packet_number_indexed_queue.h
+++ b/quic/core/packet_number_indexed_queue.h
@@ -34,6 +34,8 @@
// just two entries will cause it to consume all of the memory available.
// Because of that, it is not a general-purpose container and should not be used
// as one.
+// TODO(wub): Update the comments when deprecating
+// --quic_bw_sampler_remove_packets_once_per_congestion_event.
template <typename T>
class QUIC_NO_EXPORT PacketNumberIndexedQueue {
public:
@@ -60,6 +62,9 @@
template <typename Function>
bool Remove(QuicPacketNumber packet_number, Function f);
+ // Remove up to, but not including |packet_number|.
+ void RemoveUpTo(QuicPacketNumber packet_number);
+
bool IsEmpty() const { return number_of_present_entries_ == 0; }
// Returns the number of entries in the queue.
@@ -87,6 +92,9 @@
private:
// Wrapper around T used to mark whether the entry is actually in the map.
struct QUIC_NO_EXPORT EntryWrapper : T {
+ // NOTE(wub): When quic_bw_sampler_remove_packets_once_per_congestion_event
+ // is enabled, |present| is false if and only if this is a placeholder entry
+ // for holes in the parent's |entries|.
bool present;
EntryWrapper() : present(false) {}
@@ -106,6 +114,9 @@
}
QuicDeque<EntryWrapper> entries_;
+ // NOTE(wub): When --quic_bw_sampler_remove_packets_once_per_congestion_event
+ // is enabled, |number_of_present_entries_| only represents number of holes,
+ // which does not include number of acked or lost packets.
size_t number_of_present_entries_;
QuicPacketNumber first_packet_;
};
@@ -189,6 +200,21 @@
}
template <typename T>
+void PacketNumberIndexedQueue<T>::RemoveUpTo(QuicPacketNumber packet_number) {
+ while (!entries_.empty() && first_packet_.IsInitialized() &&
+ first_packet_ < packet_number) {
+ if (entries_.front().present) {
+ number_of_present_entries_--;
+ }
+ entries_.pop_front();
+ first_packet_++;
+ }
+ if (entries_.empty()) {
+ first_packet_.Clear();
+ }
+}
+
+template <typename T>
void PacketNumberIndexedQueue<T>::Cleanup() {
while (!entries_.empty() && !entries_.front().present) {
entries_.pop_front();
diff --git a/quic/core/packet_number_indexed_queue_test.cc b/quic/core/packet_number_indexed_queue_test.cc
index c293133..e23df99 100644
--- a/quic/core/packet_number_indexed_queue_test.cc
+++ b/quic/core/packet_number_indexed_queue_test.cc
@@ -8,6 +8,7 @@
#include <map>
#include <string>
+#include "net/third_party/quiche/src/quic/core/quic_packet_number.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
namespace quic {
@@ -167,6 +168,29 @@
ASSERT_FALSE(queue_.Remove(QuicPacketNumber(1001)));
}
+TEST_F(PacketNumberIndexedQueueTest, RemoveUpTo) {
+ queue_.Emplace(QuicPacketNumber(1001), "one");
+ queue_.Emplace(QuicPacketNumber(2001), "two");
+ EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet());
+ EXPECT_EQ(2u, queue_.number_of_present_entries());
+
+ queue_.RemoveUpTo(QuicPacketNumber(1001));
+ EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet());
+ EXPECT_EQ(2u, queue_.number_of_present_entries());
+
+ queue_.RemoveUpTo(QuicPacketNumber(1100));
+ EXPECT_EQ(QuicPacketNumber(1100u), queue_.first_packet());
+ EXPECT_EQ(1u, queue_.number_of_present_entries());
+
+ queue_.RemoveUpTo(QuicPacketNumber(2001));
+ EXPECT_EQ(QuicPacketNumber(2001u), queue_.first_packet());
+ EXPECT_EQ(1u, queue_.number_of_present_entries());
+
+ queue_.RemoveUpTo(QuicPacketNumber(2002));
+ EXPECT_FALSE(queue_.first_packet().IsInitialized());
+ EXPECT_EQ(0u, queue_.number_of_present_entries());
+}
+
TEST_F(PacketNumberIndexedQueueTest, ConstGetter) {
queue_.Emplace(QuicPacketNumber(1001), "one");
const auto& const_queue = queue_;