gfe-relnote: In QUIC, making sure there are always enough credits for non-loss retransmission. Protected by gfe2_reloadable_flag_quic_grant_enough_credits.
PiperOrigin-RevId: 269791493
Change-Id: I9e841bdc1110ae7600415b428e2371573a8a8687
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index a67fc52..fd29785 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -9117,7 +9117,8 @@
TEST_P(QuicConnectionTest, RtoPacketAsTwo) {
if (!QuicConnectionPeer::GetSentPacketManager(&connection_)
->fix_rto_retransmission() ||
- connection_.PtoEnabled()) {
+ connection_.PtoEnabled() ||
+ GetQuicReloadableFlag(quic_grant_enough_credits)) {
return;
}
connection_.SetMaxTailLossProbes(1);
@@ -9190,6 +9191,62 @@
EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
}
+TEST_P(QuicConnectionTest, RetransmitPacketAsTwo) {
+ if (!connection_.session_decides_what_to_write() ||
+ !GetQuicReloadableFlag(quic_grant_enough_credits)) {
+ return;
+ }
+ connection_.SetMaxTailLossProbes(1);
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ std::string stream_data(3000, 's');
+ // Send packets 1 - 66 and exhaust cwnd.
+ for (size_t i = 0; i < 22; ++i) {
+ // 3 packets for each stream, the first 2 are guaranteed to be full packets.
+ SendStreamDataToPeer(i + 2, stream_data, 0, FIN, nullptr);
+ }
+ CongestionBlockWrites();
+
+ if (connection_.PtoEnabled()) {
+ // Fires PTO, packets 1 and 2 are retransmitted as 4 packets.
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, QuicPacketNumber(67), _, _));
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, QuicPacketNumber(68), _, _));
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, QuicPacketNumber(69), _, _));
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, QuicPacketNumber(70), _, _));
+ } else {
+ // Fires TLP. Verify 2 packets gets sent.
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, QuicPacketNumber(67), _, _));
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, QuicPacketNumber(68), _, _));
+ }
+ connection_.GetRetransmissionAlarm()->Fire();
+
+ if (connection_.PtoEnabled()) {
+ // Fires PTO, packets 3 and 4 are retransmitted as 3 packets.
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, QuicPacketNumber(71), _, _));
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, QuicPacketNumber(72), _, _));
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, QuicPacketNumber(73), _, _));
+ } else {
+ // Fires RTO. Verify packets 2 and 3 are retransmitted as 3 packets.
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, QuicPacketNumber(69), _, _));
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, QuicPacketNumber(70), _, _));
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, QuicPacketNumber(71), _, _));
+ }
+ connection_.GetRetransmissionAlarm()->Fire();
+ EXPECT_EQ(
+ 0u, connection_.sent_packet_manager().pending_timer_transmission_count());
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc
index ee7bb34..8731306 100644
--- a/quic/core/quic_sent_packet_manager.cc
+++ b/quic/core/quic_sent_packet_manager.cc
@@ -67,6 +67,31 @@
(unacked_packets_.perspective() == Perspective::IS_SERVER ? "Server: " \
: "Client: ")
+QuicSentPacketManager::ScopedCreditGrantor::ScopedCreditGrantor(
+ QuicSentPacketManager* manager)
+ : manager_(manager), credits_granted_(false) {
+ if (!GetQuicReloadableFlag(quic_grant_enough_credits)) {
+ return;
+ }
+ QUIC_RELOADABLE_FLAG_COUNT(quic_grant_enough_credits);
+ if (manager_->pending_timer_transmission_count() > 1) {
+ // There are enough credits to retransmit one packet.
+ return;
+ }
+ // Grant 2 credits because a single packet can be transmitted as 2 (if packet
+ // number length changes).
+ manager_->set_pending_timer_transmission_count(2);
+ credits_granted_ = true;
+}
+
+QuicSentPacketManager::ScopedCreditGrantor::~ScopedCreditGrantor() {
+ if (!credits_granted_) {
+ // Do not clear credits if there is no credit granted.
+ return;
+ }
+ manager_->set_pending_timer_transmission_count(0);
+}
+
QuicSentPacketManager::QuicSentPacketManager(
Perspective perspective,
const QuicClock* clock,
@@ -525,6 +550,7 @@
// applications may want to use higher priority stream data for bandwidth
// probing, and some applications want to consider RTO is an indication of
// loss, etc.
+ ScopedCreditGrantor grantor(this);
unacked_packets_.RetransmitFrames(*transmission_info, transmission_type);
return;
}
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h
index cbb00a9..0199648 100644
--- a/quic/core/quic_sent_packet_manager.h
+++ b/quic/core/quic_sent_packet_manager.h
@@ -108,6 +108,19 @@
PTO_MODE,
};
+ // Grantor makes sure there are enough credits to retransmit a packet. When
+ // grantor is out of scope, remaining credits will be cleared.
+ class QUIC_EXPORT_PRIVATE ScopedCreditGrantor {
+ public:
+ explicit ScopedCreditGrantor(QuicSentPacketManager* manager);
+ ~ScopedCreditGrantor();
+
+ private:
+ QuicSentPacketManager* manager_;
+ // Indicates whether any credit has been granted.
+ bool credits_granted_;
+ };
+
QuicSentPacketManager(Perspective perspective,
const QuicClock* clock,
QuicRandom* random,
@@ -373,6 +386,10 @@
return pending_timer_transmission_count_;
}
+ void set_pending_timer_transmission_count(size_t count) {
+ pending_timer_transmission_count_ = count;
+ }
+
QuicTime::Delta peer_max_ack_delay() const { return peer_max_ack_delay_; }
void set_peer_max_ack_delay(QuicTime::Delta peer_max_ack_delay) {