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) {