Fix QUIC_BUG in FlushAckFrame to check whether frames is empty. Protected by FLAGS_quic_reloadable_flag_quic_single_ack_in_packet2. PiperOrigin-RevId: 349583801 Change-Id: I75958100168a1c77c8661d217224f6584fac284c
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc index 9342bd6..3ea815b 100644 --- a/quic/core/quic_connection.cc +++ b/quic/core/quic_connection.cc
@@ -3927,8 +3927,8 @@ bool send_ack = error != QUIC_PACKET_WRITE_ERROR && !uber_received_packet_manager_.IsAckFrameEmpty( QuicUtils::GetPacketNumberSpace(encryption_level_)); - if (GetQuicReloadableFlag(quic_single_ack_in_packet)) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_single_ack_in_packet, 1, 2); + if (GetQuicReloadableFlag(quic_single_ack_in_packet2)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_single_ack_in_packet2, 1, 2); send_ack = !packet_creator_.has_ack() && send_ack; } if (send_ack) { @@ -3974,8 +3974,8 @@ bool send_ack = error != QUIC_PACKET_WRITE_ERROR && !uber_received_packet_manager_.IsAckFrameEmpty( QuicUtils::GetPacketNumberSpace(encryption_level_)); - if (GetQuicReloadableFlag(quic_single_ack_in_packet)) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_single_ack_in_packet, 2, 2); + if (GetQuicReloadableFlag(quic_single_ack_in_packet2)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_single_ack_in_packet2, 2, 2); send_ack = !packet_creator_.has_ack() && send_ack; } if (send_ack) {
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc index 1baf44a..dc434a5 100644 --- a/quic/core/quic_connection_test.cc +++ b/quic/core/quic_connection_test.cc
@@ -12750,7 +12750,7 @@ ProcessFramesPacketWithAddresses(frames, kSelfAddress, kPeerAddress, ENCRYPTION_FORWARD_SECURE); ASSERT_FALSE(writer_->ack_frames().empty()); - if (GetQuicReloadableFlag(quic_single_ack_in_packet)) { + if (GetQuicReloadableFlag(quic_single_ack_in_packet2)) { EXPECT_EQ(1u, writer_->ack_frames().size()); } else { EXPECT_EQ(2u, writer_->ack_frames().size()); @@ -13070,6 +13070,33 @@ EXPECT_FALSE(connection_.connected()); } +// Regresstion test for b/175685916 +TEST_P(QuicConnectionTest, TryToFlushAckWithAckQueued) { + if (!version().HasIetfQuicFrames()) { + return; + } + SetQuicReloadableFlag(quic_can_send_ack_frequency, true); + SetQuicReloadableFlag(quic_single_ack_in_packet2, true); + set_perspective(Perspective::IS_SERVER); + + QuicConfig config; + QuicConfigPeer::SetReceivedMinAckDelayMs(&config, /*min_ack_delay_ms=*/1); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + connection_.OnHandshakeComplete(); + QuicPacketCreatorPeer::SetPacketNumber(creator_, 200); + + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_FORWARD_SECURE); + // Sending ACK_FREQUENCY bundles ACK. QuicConnectionPeer::SendPing + // will try to bundle ACK but there is no pending ACK. + EXPECT_CALL(visitor_, SendAckFrequency(_)) + .WillOnce(Invoke(¬ifier_, + &SimpleSessionNotifier::WriteOrBufferAckFrequency)); + QuicConnectionPeer::SendPing(&connection_); +} + } // namespace } // namespace test } // namespace quic
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h index 1397140..27383de 100644 --- a/quic/core/quic_flags_list.h +++ b/quic/core/quic_flags_list.h
@@ -55,7 +55,7 @@ QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_path_response, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_timestamps, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_version_negotiation_for_short_connection_ids, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_single_ack_in_packet, false) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_single_ack_in_packet2, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_split_up_send_rst_2, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_start_peer_migration_earlier, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_testonly_default_false, false)
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc index 0ebfc30..7dbe37a 100644 --- a/quic/core/quic_packet_creator.cc +++ b/quic/core/quic_packet_creator.cc
@@ -1423,7 +1423,10 @@ bool QuicPacketCreator::FlushAckFrame(const QuicFrames& frames) { QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when " "generator tries to send ACK frame."; - QUIC_BUG_IF(GetQuicReloadableFlag(quic_single_ack_in_packet) && has_ack()) + // MaybeBundleAckOpportunistically could be called nestedly when sending a + // control frame causing another control frame to be sent. + QUIC_BUG_IF(GetQuicReloadableFlag(quic_single_ack_in_packet2) && + !frames.empty() && has_ack()) << "Trying to flush " << frames << " when there is ACK queued"; for (const auto& frame : frames) { DCHECK(frame.type == ACK_FRAME || frame.type == STOP_WAITING_FRAME);
diff --git a/quic/test_tools/simple_session_notifier.cc b/quic/test_tools/simple_session_notifier.cc index ccb784f..1a75a4c 100644 --- a/quic/test_tools/simple_session_notifier.cc +++ b/quic/test_tools/simple_session_notifier.cc
@@ -125,6 +125,24 @@ WriteBufferedControlFrames(); } +void SimpleSessionNotifier::WriteOrBufferAckFrequency( + const QuicAckFrequencyFrame& ack_frequency_frame) { + QUIC_DVLOG(1) << "Writing ACK_FREQUENCY"; + const bool had_buffered_data = + HasBufferedStreamData() || HasBufferedControlFrames(); + QuicControlFrameId control_frame_id = ++last_control_frame_id_; + control_frames_.emplace_back(( + QuicFrame(new QuicAckFrequencyFrame(control_frame_id, + /*sequence_number=*/control_frame_id, + ack_frequency_frame.packet_tolerance, + ack_frequency_frame.max_ack_delay)))); + if (had_buffered_data) { + QUIC_DLOG(WARNING) << "Connection is write blocked"; + return; + } + WriteBufferedControlFrames(); +} + void SimpleSessionNotifier::NeuterUnencryptedData() { if (QuicVersionUsesCryptoFrames(connection_->transport_version())) { for (const auto& interval : crypto_bytes_transferred_[ENCRYPTION_INITIAL]) {
diff --git a/quic/test_tools/simple_session_notifier.h b/quic/test_tools/simple_session_notifier.h index ac2f18a..5f7d4ae 100644 --- a/quic/test_tools/simple_session_notifier.h +++ b/quic/test_tools/simple_session_notifier.h
@@ -35,6 +35,10 @@ // Tries to write PING. void WriteOrBufferPing(); + // Tries to write ACK_FREQUENCY. + void WriteOrBufferAckFrequency( + const QuicAckFrequencyFrame& ack_frequency_frame); + // Tries to write CRYPTO data and returns the number of bytes written. size_t WriteCryptoData(EncryptionLevel level, QuicByteCount data_length,