gfe-relnote: only respect TLPR when there is pending non-crypto stream data. Flag protected by quic_ignore_tlpr_if_no_pending_stream_data and session_decides_what_to_write.

When calculating the tail loss probe delay and TLPR option is enabled, only use half tail loss probe when session has pending stream data. Otherwise, ignore TLPR.

PiperOrigin-RevId: 253286073
Change-Id: I9321c9d0608f68bb4ec0f7f4fbba1e470e4a0a3c
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index fad4a3b..c7f98ed 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -2874,19 +2874,7 @@
 
 void QuicConnection::OnPingTimeout() {
   if (!retransmission_alarm_->IsSet()) {
-    bool enable_half_rtt_tail_loss_probe =
-        sent_packet_manager_.enable_half_rtt_tail_loss_probe();
-    if (enable_half_rtt_tail_loss_probe &&
-        GetQuicReloadableFlag(quic_ignore_tlpr_if_sending_ping)) {
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_ignore_tlpr_if_sending_ping, 1, 2);
-      sent_packet_manager_.set_enable_half_rtt_tail_loss_probe(false);
-    }
     visitor_->SendPing();
-    if (enable_half_rtt_tail_loss_probe &&
-        GetQuicReloadableFlag(quic_ignore_tlpr_if_sending_ping)) {
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_ignore_tlpr_if_sending_ping, 2, 2);
-      sent_packet_manager_.set_enable_half_rtt_tail_loss_probe(true);
-    }
   }
 }
 
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 6cbb209..f96a7e6 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -3898,6 +3898,10 @@
 }
 
 TEST_P(QuicConnectionTest, TailLossProbeDelayForStreamDataInTLPR) {
+  if (!connection_.session_decides_what_to_write()) {
+    return;
+  }
+
   // Set TLPR from QuicConfig.
   EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
   QuicConfig config;
@@ -3929,6 +3933,10 @@
 }
 
 TEST_P(QuicConnectionTest, TailLossProbeDelayForNonStreamDataInTLPR) {
+  if (!connection_.session_decides_what_to_write()) {
+    return;
+  }
+
   // Set TLPR from QuicConfig.
   EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
   QuicConfig config;
@@ -4014,7 +4022,7 @@
   QuicTime::Delta min_rto_timeout =
       QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs);
   srtt = manager_->GetRttStats()->SmoothedOrInitialRtt();
-  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_sending_ping)) {
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
     // First TLP without unacked stream data will no longer use TLPR.
     expected_delay = std::max(2 * srtt, 1.5 * srtt + 0.5 * min_rto_timeout);
   } else {
@@ -4025,11 +4033,7 @@
   EXPECT_EQ(expected_delay,
             connection_.GetRetransmissionAlarm()->deadline() - clock_.Now());
 
-  // Verify the path degrading delay.
-  // Path degrading delay will count TLPR for the tail loss probe delay.
-  expected_delay =
-      std::max(QuicTime::Delta::FromMilliseconds(kMinTailLossProbeTimeoutMs),
-               srtt * 0.5);
+  // Verify the path degrading delay = TLP delay + 1st RTO + 2nd RTO.
   // Add 1st RTO.
   retransmission_delay =
       std::max(manager_->GetRttStats()->smoothed_rtt() +
@@ -4047,6 +4051,26 @@
   EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
   EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs),
             connection_.GetPingAlarm()->deadline() - clock_.ApproximateNow());
+
+  // Advance a small period of time: 5ms. And receive a retransmitted ACK.
+  // This will update the retransmission alarm, verify the retransmission delay
+  // is correct.
+  clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+  QuicAckFrame ack = InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(2)}});
+  ProcessAckPacket(&ack);
+
+  // Verify the retransmission delay.
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    // First TLP without unacked stream data will no longer use TLPR.
+    expected_delay = std::max(2 * srtt, 1.5 * srtt + 0.5 * min_rto_timeout);
+  } else {
+    expected_delay =
+        std::max(QuicTime::Delta::FromMilliseconds(kMinTailLossProbeTimeoutMs),
+                 srtt * 0.5);
+  }
+  expected_delay = expected_delay - QuicTime::Delta::FromMilliseconds(5);
+  EXPECT_EQ(expected_delay,
+            connection_.GetRetransmissionAlarm()->deadline() - clock_.Now());
 }
 
 TEST_P(QuicConnectionTest, RTO) {
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc
index c8ae53b..d92ee66 100644
--- a/quic/core/quic_sent_packet_manager.cc
+++ b/quic/core/quic_sent_packet_manager.cc
@@ -118,7 +118,9 @@
       acked_packets_iter_(last_ack_frame_.packets.rbegin()),
       tolerate_reneging_(GetQuicReloadableFlag(quic_tolerate_reneging)),
       loss_removes_from_inflight_(
-          GetQuicReloadableFlag(quic_loss_removes_from_inflight)) {
+          GetQuicReloadableFlag(quic_loss_removes_from_inflight)),
+      ignore_tlpr_if_no_pending_stream_data_(
+          GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
   if (tolerate_reneging_) {
     QUIC_RELOADABLE_FLAG_COUNT(quic_tolerate_reneging);
   }
@@ -1017,7 +1019,16 @@
     size_t consecutive_tlp_count) const {
   QuicTime::Delta srtt = rtt_stats_.SmoothedOrInitialRtt();
   if (enable_half_rtt_tail_loss_probe_ && consecutive_tlp_count == 0u) {
-    return std::max(min_tlp_timeout_, srtt * 0.5);
+    if (!ignore_tlpr_if_no_pending_stream_data_ ||
+        !session_decides_what_to_write()) {
+      return std::max(min_tlp_timeout_, srtt * 0.5);
+    }
+    QUIC_RELOADABLE_FLAG_COUNT_N(quic_ignore_tlpr_if_no_pending_stream_data, 1,
+                                 5);
+    if (unacked_packets().HasUnackedStreamData()) {
+      // Enable TLPR if there are pending data packets.
+      return std::max(min_tlp_timeout_, srtt * 0.5);
+    }
   }
   if (ietf_style_tlp_) {
     return std::max(min_tlp_timeout_, 1.5 * srtt + rtt_stats_.max_ack_delay());
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h
index 787bf8d..bba4887 100644
--- a/quic/core/quic_sent_packet_manager.h
+++ b/quic/core/quic_sent_packet_manager.h
@@ -360,15 +360,6 @@
     delayed_ack_time_ = delayed_ack_time;
   }
 
-  bool enable_half_rtt_tail_loss_probe() const {
-    return enable_half_rtt_tail_loss_probe_;
-  }
-
-  void set_enable_half_rtt_tail_loss_probe(
-      bool enable_half_rtt_tail_loss_probe) {
-    enable_half_rtt_tail_loss_probe_ = enable_half_rtt_tail_loss_probe;
-  }
-
   const QuicUnackedPacketMap& unacked_packets() const {
     return unacked_packets_;
   }
@@ -393,6 +384,10 @@
     return unacked_packets_.use_uber_loss_algorithm();
   }
 
+  bool ignore_tlpr_if_no_pending_stream_data() const {
+    return ignore_tlpr_if_no_pending_stream_data_;
+  }
+
  private:
   friend class test::QuicConnectionPeer;
   friend class test::QuicSentPacketManagerPeer;
@@ -637,6 +632,9 @@
 
   // Latched value of quic_loss_removes_from_inflight.
   const bool loss_removes_from_inflight_;
+
+  // Latched value of quic_ignore_tlpr_if_no_pending_stream_data.
+  const bool ignore_tlpr_if_no_pending_stream_data_;
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc
index a0746fe..1e8302f 100644
--- a/quic/core/quic_sent_packet_manager_test.cc
+++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -267,6 +267,14 @@
     return packet;
   }
 
+  SerializedPacket CreatePingPacket(uint64_t packet_number) {
+    SerializedPacket packet(QuicPacketNumber(packet_number),
+                            PACKET_4BYTE_PACKET_NUMBER, nullptr, kDefaultLength,
+                            false, false);
+    packet.retransmittable_frames.push_back(QuicFrame(QuicPingFrame()));
+    return packet;
+  }
+
   void SendDataPacket(uint64_t packet_number) {
     SendDataPacket(packet_number, ENCRYPTION_INITIAL);
   }
@@ -282,6 +290,17 @@
                           NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
   }
 
+  void SendPingPacket(uint64_t packet_number,
+                      EncryptionLevel encryption_level) {
+    EXPECT_CALL(*send_algorithm_,
+                OnPacketSent(_, BytesInFlight(),
+                             QuicPacketNumber(packet_number), _, _));
+    SerializedPacket packet(CreatePingPacket(packet_number));
+    packet.encryption_level = encryption_level;
+    manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(),
+                          NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+  }
+
   void SendCryptoPacket(uint64_t packet_number) {
     EXPECT_CALL(
         *send_algorithm_,
@@ -1761,6 +1780,118 @@
   EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
 }
 
+TEST_P(QuicSentPacketManagerTest, TLPRWithPendingStreamData) {
+  if (!manager_.session_decides_what_to_write()) {
+    return;
+  }
+
+  QuicConfig config;
+  QuicTagVector options;
+
+  options.push_back(kTLPR);
+  QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+  EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+  EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+  EXPECT_CALL(*send_algorithm_, PacingRate(_))
+      .WillRepeatedly(Return(QuicBandwidth::Zero()));
+  EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+      .WillOnce(Return(10 * kDefaultTCPMSS));
+  manager_.SetFromConfig(config);
+  EXPECT_TRUE(
+      QuicSentPacketManagerPeer::GetEnableHalfRttTailLossProbe(&manager_));
+
+  QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
+
+  SendDataPacket(1);
+  SendDataPacket(2);
+
+  // Test with a standard smoothed RTT.
+  RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats());
+  rtt_stats->set_initial_rtt(QuicTime::Delta::FromMilliseconds(100));
+  QuicTime::Delta srtt = rtt_stats->initial_rtt();
+  // With pending stream data, TLPR is used.
+  QuicTime::Delta expected_tlp_delay = 0.5 * srtt;
+  EXPECT_CALL(notifier_, HasUnackedStreamData()).WillRepeatedly(Return(true));
+
+  EXPECT_EQ(expected_tlp_delay,
+            manager_.GetRetransmissionTime() - clock_.Now());
+
+  // Retransmit the packet by invoking the retransmission timeout.
+  clock_.AdvanceTime(expected_tlp_delay);
+  manager_.OnRetransmissionTimeout();
+  EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now()));
+  EXPECT_FALSE(manager_.HasPendingRetransmissions());
+  EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+      .WillOnce(WithArgs<1>(Invoke(
+          [this](TransmissionType type) { RetransmitDataPacket(3, type); })));
+  EXPECT_TRUE(manager_.MaybeRetransmitTailLossProbe());
+
+  EXPECT_CALL(*send_algorithm_, CanSend(_)).WillOnce(Return(false));
+  EXPECT_EQ(QuicTime::Delta::Infinite(), manager_.TimeUntilSend(clock_.Now()));
+  EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+  // 2nd TLP.
+  expected_tlp_delay = 2 * srtt;
+  EXPECT_EQ(expected_tlp_delay,
+            manager_.GetRetransmissionTime() - clock_.Now());
+}
+
+TEST_P(QuicSentPacketManagerTest, TLPRWithoutPendingStreamData) {
+  if (!manager_.session_decides_what_to_write()) {
+    return;
+  }
+
+  QuicConfig config;
+  QuicTagVector options;
+
+  options.push_back(kTLPR);
+  QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+  EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+  EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+  EXPECT_CALL(*send_algorithm_, PacingRate(_))
+      .WillRepeatedly(Return(QuicBandwidth::Zero()));
+  EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+      .WillOnce(Return(10 * kDefaultTCPMSS));
+  manager_.SetFromConfig(config);
+  EXPECT_TRUE(
+      QuicSentPacketManagerPeer::GetEnableHalfRttTailLossProbe(&manager_));
+  QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
+
+  SendPingPacket(1, ENCRYPTION_INITIAL);
+  SendPingPacket(2, ENCRYPTION_INITIAL);
+
+  // Test with a standard smoothed RTT.
+  RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats());
+  rtt_stats->set_initial_rtt(QuicTime::Delta::FromMilliseconds(100));
+  QuicTime::Delta srtt = rtt_stats->initial_rtt();
+  QuicTime::Delta expected_tlp_delay = 0.5 * srtt;
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    // With no pending stream data, TLPR is ignored.
+    expected_tlp_delay = 2 * srtt;
+  }
+  EXPECT_CALL(notifier_, HasUnackedStreamData()).WillRepeatedly(Return(false));
+  EXPECT_EQ(expected_tlp_delay,
+            manager_.GetRetransmissionTime() - clock_.Now());
+
+  // Retransmit the packet by invoking the retransmission timeout.
+  clock_.AdvanceTime(expected_tlp_delay);
+  manager_.OnRetransmissionTimeout();
+  EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now()));
+  EXPECT_FALSE(manager_.HasPendingRetransmissions());
+  EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+      .WillOnce(WithArgs<1>(Invoke(
+          [this](TransmissionType type) { RetransmitDataPacket(3, type); })));
+  EXPECT_TRUE(manager_.MaybeRetransmitTailLossProbe());
+  EXPECT_CALL(*send_algorithm_, CanSend(_)).WillOnce(Return(false));
+  EXPECT_EQ(QuicTime::Delta::Infinite(), manager_.TimeUntilSend(clock_.Now()));
+  EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+  // 2nd TLP.
+  expected_tlp_delay = 2 * srtt;
+  EXPECT_EQ(expected_tlp_delay,
+            manager_.GetRetransmissionTime() - clock_.Now());
+}
+
 TEST_P(QuicSentPacketManagerTest, GetTransmissionTimeSpuriousRTO) {
   RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats());
   rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index 33b9ddb..1e863b1 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -8,9 +8,11 @@
 #include <string>
 #include <utility>
 
+#include "base/logging.h"
 #include "net/third_party/quiche/src/quic/core/quic_connection.h"
 #include "net/third_party/quiche/src/quic/core/quic_flow_controller.h"
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
@@ -869,6 +871,13 @@
   if (stream->IsWaitingForAcks()) {
     zombie_streams_[stream->id()] = std::move(it->second);
   } else {
+    // Clean up the stream since it is no longer waiting for acks.
+    if (ignore_tlpr_if_no_pending_stream_data() &&
+        session_decides_what_to_write()) {
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_ignore_tlpr_if_no_pending_stream_data,
+                                   2, 5);
+      streams_waiting_for_acks_.erase(stream->id());
+    }
     closed_streams_.push_back(std::move(it->second));
     // Do not retransmit data of a closed stream.
     streams_with_pending_retransmission_.erase(stream_id);
@@ -1543,6 +1552,13 @@
 }
 
 void QuicSession::OnStreamDoneWaitingForAcks(QuicStreamId id) {
+  if (ignore_tlpr_if_no_pending_stream_data() &&
+      session_decides_what_to_write()) {
+    QUIC_RELOADABLE_FLAG_COUNT_N(quic_ignore_tlpr_if_no_pending_stream_data, 3,
+                                 5);
+    streams_waiting_for_acks_.erase(id);
+  }
+
   auto it = zombie_streams_.find(id);
   if (it == zombie_streams_.end()) {
     return;
@@ -1557,6 +1573,34 @@
   streams_with_pending_retransmission_.erase(id);
 }
 
+void QuicSession::OnStreamWaitingForAcks(QuicStreamId id) {
+  if (!ignore_tlpr_if_no_pending_stream_data() ||
+      !session_decides_what_to_write())
+    return;
+
+  // Exclude crypto stream's status since it is counted in HasUnackedCryptoData.
+  if (GetCryptoStream() != nullptr && id == GetCryptoStream()->id()) {
+    return;
+  }
+
+  QUIC_RELOADABLE_FLAG_COUNT_N(quic_ignore_tlpr_if_no_pending_stream_data, 4,
+                               5);
+  streams_waiting_for_acks_.insert(id);
+
+  // The number of the streams waiting for acks should not be larger than the
+  // number of streams.
+  if (dynamic_stream_map_.size() + static_stream_map_.size() +
+          zombie_streams_.size() <
+      static_cast<int>(streams_waiting_for_acks_.size())) {
+    QUIC_BUG << "More streams are waiting for acks than the number of streams. "
+             << "Sizes: dynamic streams: " << dynamic_stream_map_.size()
+             << ", static streams: " << static_stream_map_.size()
+             << ", zombie streams: " << zombie_streams_.size()
+             << ", vs streams waiting for acks: "
+             << streams_waiting_for_acks_.size();
+  }
+}
+
 QuicStream* QuicSession::GetStream(QuicStreamId id) const {
   if (id <= largest_static_stream_id_) {
     auto static_stream = static_stream_map_.find(id);
@@ -1716,6 +1760,17 @@
   return false;
 }
 
+bool QuicSession::HasUnackedStreamData() const {
+  DCHECK(ignore_tlpr_if_no_pending_stream_data());
+  if (ignore_tlpr_if_no_pending_stream_data()) {
+    QUIC_RELOADABLE_FLAG_COUNT_N(quic_ignore_tlpr_if_no_pending_stream_data, 5,
+                                 5);
+    return !streams_waiting_for_acks_.empty();
+  }
+
+  return true;
+}
+
 WriteStreamDataResult QuicSession::WriteStreamData(QuicStreamId id,
                                                    QuicStreamOffset offset,
                                                    QuicByteCount data_length,
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index ef4e804..a76e7df 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -150,6 +150,7 @@
                         TransmissionType type) override;
   bool IsFrameOutstanding(const QuicFrame& frame) const override;
   bool HasUnackedCryptoData() const override;
+  bool HasUnackedStreamData() const override;
 
   // Called on every incoming packet. Passes |packet| through to |connection_|.
   virtual void ProcessUdpPacket(const QuicSocketAddress& self_address,
@@ -324,6 +325,9 @@
   // a stream is reset because of an error).
   void OnStreamDoneWaitingForAcks(QuicStreamId id);
 
+  // Called when stream |id| is newly waiting for acks.
+  void OnStreamWaitingForAcks(QuicStreamId id);
+
   // Called to cancel retransmission of unencypted crypto stream data.
   void NeuterUnencryptedData();
 
@@ -616,6 +620,11 @@
   // stream.
   void PendingStreamOnRstStream(const QuicRstStreamFrame& frame);
 
+  bool ignore_tlpr_if_no_pending_stream_data() const {
+    return connection_->sent_packet_manager()
+        .ignore_tlpr_if_no_pending_stream_data();
+  }
+
   // Keep track of highest received byte offset of locally closed streams, while
   // waiting for a definitive final highest offset from the peer.
   std::map<QuicStreamId, QuicStreamOffset>
@@ -654,6 +663,9 @@
   // been consumed.
   QuicUnorderedSet<QuicStreamId> draining_streams_;
 
+  // Set of stream ids that are waiting for acks excluding crypto stream id.
+  QuicUnorderedSet<QuicStreamId> streams_waiting_for_acks_;
+
   // TODO(fayang): Consider moving LegacyQuicStreamIdManager into
   // UberQuicStreamIdManager.
   // Manages stream IDs for Google QUIC.
@@ -719,7 +731,7 @@
   // list may be a superset of the connection framer's supported versions.
   ParsedQuicVersionVector supported_versions_;
 
-  //  Latched value of quic_eliminate_static_stream_map.
+  // Latched value of quic_eliminate_static_stream_map.
   const bool eliminate_static_stream_map_;
 };
 
diff --git a/quic/core/quic_stream.cc b/quic/core/quic_stream.cc
index af8079c..55e910c 100644
--- a/quic/core/quic_stream.cc
+++ b/quic/core/quic_stream.cc
@@ -1011,6 +1011,10 @@
   if (consumed_data.bytes_consumed > 0 || consumed_data.fin_consumed) {
     busy_counter_ = 0;
   }
+
+  if (IsWaitingForAcks()) {
+    session_->OnStreamWaitingForAcks(id_);
+  }
 }
 
 uint64_t QuicStream::BufferedDataBytes() const {
diff --git a/quic/core/quic_stream_test.cc b/quic/core/quic_stream_test.cc
index f13d867..d11a7a4 100644
--- a/quic/core/quic_stream_test.cc
+++ b/quic/core/quic_stream_test.cc
@@ -314,6 +314,9 @@
                                             NO_FIN);
       }));
   stream_->WriteOrBufferData(QuicStringPiece(kData1, 2), false, nullptr);
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
   ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams());
   EXPECT_EQ(1u, stream_->BufferedDataBytes());
 }
@@ -331,6 +334,9 @@
                                             NO_FIN);
       }));
   stream_->WriteOrBufferData(QuicStringPiece(kData1, 2), true, nullptr);
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
   ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams());
 }
 
@@ -376,6 +382,10 @@
                                             kDataLen - 1, 0u, NO_FIN);
       }));
   stream_->WriteOrBufferData(kData1, false, nullptr);
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
+
   EXPECT_EQ(1u, stream_->BufferedDataBytes());
   EXPECT_TRUE(HasWriteBlockedStreams());
 
@@ -390,6 +400,9 @@
                                             kDataLen - 1, kDataLen - 1, NO_FIN);
       }));
   stream_->OnCanWrite();
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
 
   // And finally the end of the bytes_consumed.
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
@@ -398,6 +411,9 @@
                                             2 * kDataLen - 2, NO_FIN);
       }));
   stream_->OnCanWrite();
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
 }
 
 TEST_P(QuicStreamTest, WriteOrBufferDataReachStreamLimit) {
@@ -408,6 +424,9 @@
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
       .WillOnce(Invoke(&(MockQuicSession::ConsumeData)));
   stream_->WriteOrBufferData(data, false, nullptr);
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
   EXPECT_CALL(*connection_, CloseConnection(QUIC_STREAM_LENGTH_OVERFLOW, _, _));
   EXPECT_QUIC_BUG(stream_->WriteOrBufferData("a", false, nullptr),
                   "Write too many data via stream");
@@ -442,12 +461,18 @@
                                             NO_FIN);
       }));
   stream_->WriteOrBufferData(QuicStringPiece(kData1, 1), false, nullptr);
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
   EXPECT_FALSE(fin_sent());
   EXPECT_FALSE(rst_sent());
 
   // Now close the stream, and expect that we send a RST.
   EXPECT_CALL(*session_, SendRstStream(_, _, _));
   stream_->OnClose();
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_FALSE(session_->HasUnackedStreamData());
+  }
   EXPECT_FALSE(fin_sent());
   EXPECT_TRUE(rst_sent());
 }
@@ -818,9 +843,15 @@
   // Stream is not waiting for acks initially.
   EXPECT_FALSE(stream_->IsWaitingForAcks());
   EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_FALSE(session_->HasUnackedStreamData());
+  }
 
   // Send kData1.
   stream_->WriteOrBufferData(kData1, false, nullptr);
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
   EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
   EXPECT_TRUE(stream_->IsWaitingForAcks());
   QuicByteCount newly_acked_length = 0;
@@ -829,11 +860,17 @@
   EXPECT_EQ(9u, newly_acked_length);
   // Stream is not waiting for acks as all sent data is acked.
   EXPECT_FALSE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_FALSE(session_->HasUnackedStreamData());
+  }
   EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
 
   // Send kData2.
   stream_->WriteOrBufferData(kData2, false, nullptr);
   EXPECT_TRUE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
   EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
   // Send FIN.
   stream_->WriteOrBufferData("", true, nullptr);
@@ -849,6 +886,9 @@
   EXPECT_EQ(9u, newly_acked_length);
   // Stream is waiting for acks as FIN is not acked.
   EXPECT_TRUE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
   EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
 
   // FIN is acked.
@@ -856,6 +896,9 @@
                                           &newly_acked_length));
   EXPECT_EQ(0u, newly_acked_length);
   EXPECT_FALSE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_FALSE(session_->HasUnackedStreamData());
+  }
   EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
 }
 
@@ -870,26 +913,43 @@
   stream_->WriteOrBufferData("", true, nullptr);
   EXPECT_EQ(3u, QuicStreamPeer::SendBuffer(stream_).size());
   EXPECT_TRUE(stream_->IsWaitingForAcks());
-
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
   QuicByteCount newly_acked_length = 0;
   EXPECT_TRUE(stream_->OnStreamFrameAcked(9, 9, false, QuicTime::Delta::Zero(),
                                           &newly_acked_length));
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
   EXPECT_EQ(9u, newly_acked_length);
   EXPECT_EQ(3u, QuicStreamPeer::SendBuffer(stream_).size());
   EXPECT_TRUE(stream_->OnStreamFrameAcked(18, 9, false, QuicTime::Delta::Zero(),
                                           &newly_acked_length));
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
   EXPECT_EQ(9u, newly_acked_length);
   EXPECT_EQ(3u, QuicStreamPeer::SendBuffer(stream_).size());
   EXPECT_TRUE(stream_->OnStreamFrameAcked(0, 9, false, QuicTime::Delta::Zero(),
                                           &newly_acked_length));
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
   EXPECT_EQ(9u, newly_acked_length);
   EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
   // FIN is not acked yet.
   EXPECT_TRUE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
   EXPECT_TRUE(stream_->OnStreamFrameAcked(27, 0, true, QuicTime::Delta::Zero(),
                                           &newly_acked_length));
   EXPECT_EQ(0u, newly_acked_length);
   EXPECT_FALSE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_FALSE(session_->HasUnackedStreamData());
+  }
 }
 
 TEST_P(QuicStreamTest, CancelStream) {
@@ -897,22 +957,43 @@
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
       .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
   EXPECT_FALSE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_FALSE(session_->HasUnackedStreamData());
+  }
   EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
 
   stream_->WriteOrBufferData(kData1, false, nullptr);
   EXPECT_TRUE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
   EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
   // Cancel stream.
   stream_->Reset(QUIC_STREAM_NO_ERROR);
   // stream still waits for acks as the error code is QUIC_STREAM_NO_ERROR, and
   // data is going to be retransmitted.
   EXPECT_TRUE(stream_->IsWaitingForAcks());
-  EXPECT_CALL(*session_,
-              SendRstStream(stream_->id(), QUIC_STREAM_CANCELLED, 9));
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
+  EXPECT_CALL(*connection_,
+              OnStreamReset(stream_->id(), QUIC_STREAM_CANCELLED));
+  EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1);
+  EXPECT_CALL(*session_, SendRstStream(stream_->id(), QUIC_STREAM_CANCELLED, 9))
+      .WillOnce(InvokeWithoutArgs([this]() {
+        return QuicSessionPeer::SendRstStreamInner(
+            session_.get(), stream_->id(), QUIC_STREAM_CANCELLED,
+            stream_->stream_bytes_written(),
+            /*close_write_side_only=*/false);
+      }));
+
   stream_->Reset(QUIC_STREAM_CANCELLED);
   EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
   // Stream stops waiting for acks as data is not going to be retransmitted.
   EXPECT_FALSE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_FALSE(session_->HasUnackedStreamData());
+  }
 }
 
 TEST_P(QuicStreamTest, RstFrameReceivedStreamNotFinishSending) {
@@ -920,10 +1001,16 @@
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
       .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
   EXPECT_FALSE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_FALSE(session_->HasUnackedStreamData());
+  }
   EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
 
   stream_->WriteOrBufferData(kData1, false, nullptr);
   EXPECT_TRUE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
   EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
 
   // RST_STREAM received.
@@ -936,6 +1023,9 @@
   // Stream stops waiting for acks as it does not finish sending and rst is
   // sent.
   EXPECT_FALSE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_FALSE(session_->HasUnackedStreamData());
+  }
 }
 
 TEST_P(QuicStreamTest, RstFrameReceivedStreamFinishSending) {
@@ -943,10 +1033,16 @@
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
       .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
   EXPECT_FALSE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_FALSE(session_->HasUnackedStreamData());
+  }
   EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
 
   stream_->WriteOrBufferData(kData1, true, nullptr);
   EXPECT_TRUE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
 
   // RST_STREAM received.
   EXPECT_CALL(*session_, SendRstStream(_, _, _)).Times(0);
@@ -955,6 +1051,9 @@
   stream_->OnStreamReset(rst_frame);
   // Stream still waits for acks as it finishes sending and has unacked data.
   EXPECT_TRUE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
   EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
 }
 
@@ -963,11 +1062,16 @@
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
       .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
   EXPECT_FALSE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_FALSE(session_->HasUnackedStreamData());
+  }
   EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
 
   stream_->WriteOrBufferData(kData1, false, nullptr);
   EXPECT_TRUE(stream_->IsWaitingForAcks());
-
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
   EXPECT_CALL(*session_,
               SendRstStream(stream_->id(), QUIC_RST_ACKNOWLEDGEMENT, 9));
   stream_->OnConnectionClosed(QUIC_INTERNAL_ERROR,
@@ -975,6 +1079,9 @@
   EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
   // Stream stops waiting for acks as connection is going to close.
   EXPECT_FALSE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_FALSE(session_->HasUnackedStreamData());
+  }
 }
 
 TEST_P(QuicStreamTest, CanWriteNewDataAfterData) {
@@ -1003,6 +1110,8 @@
   stream_->WriteOrBufferData(data, false, nullptr);
   stream_->WriteOrBufferData(data, false, nullptr);
   stream_->WriteOrBufferData(data, false, nullptr);
+  EXPECT_TRUE(stream_->IsWaitingForAcks());
+
   // Verify all data is saved.
   EXPECT_EQ(3 * data.length() - 100, stream_->BufferedDataBytes());
 
@@ -1215,12 +1324,20 @@
   Initialize();
   EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
       .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+  EXPECT_FALSE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_FALSE(session_->HasUnackedStreamData());
+  }
+
   // Send [0, 27) and fin.
   stream_->WriteOrBufferData(kData1, false, nullptr);
   stream_->WriteOrBufferData(kData1, false, nullptr);
   stream_->WriteOrBufferData(kData1, true, nullptr);
   EXPECT_EQ(3u, QuicStreamPeer::SendBuffer(stream_).size());
   EXPECT_TRUE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
 
   // Ack [0, 9), [5, 22) and [18, 26)
   // Verify [0, 9) 9 bytes are acked.
@@ -1240,6 +1357,9 @@
   EXPECT_EQ(4u, newly_acked_length);
   EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
   EXPECT_TRUE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
 
   // Ack [0, 27). Verify [26, 27) 1 byte is acked.
   EXPECT_TRUE(stream_->OnStreamFrameAcked(26, 1, false, QuicTime::Delta::Zero(),
@@ -1247,6 +1367,9 @@
   EXPECT_EQ(1u, newly_acked_length);
   EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
   EXPECT_TRUE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_TRUE(session_->HasUnackedStreamData());
+  }
 
   // Ack Fin.
   EXPECT_TRUE(stream_->OnStreamFrameAcked(27, 0, true, QuicTime::Delta::Zero(),
@@ -1254,6 +1377,9 @@
   EXPECT_EQ(0u, newly_acked_length);
   EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
   EXPECT_FALSE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_FALSE(session_->HasUnackedStreamData());
+  }
 
   // Ack [10, 27) and fin. No new data is acked.
   EXPECT_FALSE(stream_->OnStreamFrameAcked(
@@ -1261,6 +1387,9 @@
   EXPECT_EQ(0u, newly_acked_length);
   EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
   EXPECT_FALSE(stream_->IsWaitingForAcks());
+  if (GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)) {
+    EXPECT_FALSE(session_->HasUnackedStreamData());
+  }
 }
 
 TEST_P(QuicStreamTest, OnStreamFrameLost) {
diff --git a/quic/core/quic_unacked_packet_map.h b/quic/core/quic_unacked_packet_map.h
index 17589dd..f9f718e 100644
--- a/quic/core/quic_unacked_packet_map.h
+++ b/quic/core/quic_unacked_packet_map.h
@@ -153,6 +153,12 @@
   // HasUnackedCryptoData() when session_decides_what_to_write_ is default true.
   bool HasPendingCryptoPackets() const;
 
+  // Returns true if there is any unacked non-crypto stream data.
+  bool HasUnackedStreamData() const {
+    DCHECK(session_decides_what_to_write());
+    return session_notifier_->HasUnackedStreamData();
+  }
+
   // Removes any retransmittable frames from this transmission or an associated
   // transmission.  It removes now useless transmissions, and disconnects any
   // other packets from other transmissions.
diff --git a/quic/core/session_notifier_interface.h b/quic/core/session_notifier_interface.h
index a601c3e..89b4a9c 100644
--- a/quic/core/session_notifier_interface.h
+++ b/quic/core/session_notifier_interface.h
@@ -37,6 +37,9 @@
 
   // Returns true if crypto stream is waiting for acks.
   virtual bool HasUnackedCryptoData() const = 0;
+
+  // Returns true if any stream is waiting for acks.
+  virtual bool HasUnackedStreamData() const = 0;
 };
 
 }  // namespace quic
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index cb51a3b..82bdd20 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -1061,6 +1061,7 @@
                void(const QuicFrames&, TransmissionType type));
   MOCK_CONST_METHOD1(IsFrameOutstanding, bool(const QuicFrame&));
   MOCK_CONST_METHOD0(HasUnackedCryptoData, bool());
+  MOCK_CONST_METHOD0(HasUnackedStreamData, bool());
 };
 
 // Creates a client session for testing.
diff --git a/quic/test_tools/simple_session_notifier.cc b/quic/test_tools/simple_session_notifier.cc
index c6e3801..624e4f7 100644
--- a/quic/test_tools/simple_session_notifier.cc
+++ b/quic/test_tools/simple_session_notifier.cc
@@ -434,6 +434,14 @@
   return !bytes_to_ack.Empty();
 }
 
+bool SimpleSessionNotifier::HasUnackedStreamData() const {
+  for (auto it : stream_map_) {
+    if (StreamIsWaitingForAcks(it.first))
+      return true;
+  }
+  return false;
+}
+
 bool SimpleSessionNotifier::OnControlFrameAcked(const QuicFrame& frame) {
   QuicControlFrameId id = GetControlFrameId(frame);
   if (id == kInvalidControlFrameId) {
diff --git a/quic/test_tools/simple_session_notifier.h b/quic/test_tools/simple_session_notifier.h
index 25c9941..aab3769 100644
--- a/quic/test_tools/simple_session_notifier.h
+++ b/quic/test_tools/simple_session_notifier.h
@@ -73,6 +73,7 @@
                         TransmissionType type) override;
   bool IsFrameOutstanding(const QuicFrame& frame) const override;
   bool HasUnackedCryptoData() const override;
+  bool HasUnackedStreamData() const override;
 
  private:
   struct StreamState {
diff --git a/quic/test_tools/simple_session_notifier_test.cc b/quic/test_tools/simple_session_notifier_test.cc
index d9958c8..8b931fe 100644
--- a/quic/test_tools/simple_session_notifier_test.cc
+++ b/quic/test_tools/simple_session_notifier_test.cc
@@ -88,6 +88,8 @@
   EXPECT_CALL(connection_, SendStreamData(5, 1024, 0, FIN))
       .WillOnce(Return(QuicConsumedData(1024, true)));
   notifier_.WriteOrBufferData(5, 1024, FIN);
+  EXPECT_TRUE(notifier_.StreamIsWaitingForAcks(5));
+  EXPECT_TRUE(notifier_.HasUnackedStreamData());
 
   // Reset stream 5 with no error.
   EXPECT_CALL(connection_, SendControlFrame(_))
@@ -96,10 +98,12 @@
   notifier_.WriteOrBufferRstStream(5, QUIC_STREAM_NO_ERROR, 1024);
   // Verify stream 5 is waiting for acks.
   EXPECT_TRUE(notifier_.StreamIsWaitingForAcks(5));
+  EXPECT_TRUE(notifier_.HasUnackedStreamData());
 
   // Reset stream 5 with error.
   notifier_.WriteOrBufferRstStream(5, QUIC_ERROR_PROCESSING_STREAM, 1024);
   EXPECT_FALSE(notifier_.StreamIsWaitingForAcks(5));
+  EXPECT_FALSE(notifier_.HasUnackedStreamData());
 }
 
 TEST_F(SimpleSessionNotifierTest, WriteOrBufferPing) {
@@ -158,10 +162,13 @@
                          QuicTime::Zero());
   EXPECT_TRUE(notifier_.StreamIsWaitingForAcks(
       QuicUtils::GetCryptoStreamId(connection_.transport_version())));
+  EXPECT_TRUE(notifier_.HasUnackedStreamData());
+
   // Neuters unencrypted data.
   notifier_.NeuterUnencryptedData();
   EXPECT_FALSE(notifier_.StreamIsWaitingForAcks(
       QuicUtils::GetCryptoStreamId(connection_.transport_version())));
+  EXPECT_FALSE(notifier_.HasUnackedStreamData());
 }
 
 TEST_F(SimpleSessionNotifierTest, OnCanWrite) {
@@ -178,6 +185,7 @@
   notifier_.WriteOrBufferData(
       QuicUtils::GetCryptoStreamId(connection_.transport_version()), 1024,
       NO_FIN);
+
   // Send crypto data [1024, 2048) in ENCRYPTION_ZERO_RTT.
   connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
   EXPECT_CALL(connection_, SendStreamData(QuicUtils::GetCryptoStreamId(
diff --git a/quic/test_tools/simulator/quic_endpoint.cc b/quic/test_tools/simulator/quic_endpoint.cc
index ba9d206..872a9b2 100644
--- a/quic/test_tools/simulator/quic_endpoint.cc
+++ b/quic/test_tools/simulator/quic_endpoint.cc
@@ -293,6 +293,13 @@
   return false;
 }
 
+bool QuicEndpoint::HasUnackedStreamData() const {
+  if (notifier_ != nullptr) {
+    return notifier_->HasUnackedStreamData();
+  }
+  return false;
+}
+
 QuicEndpoint::Writer::Writer(QuicEndpoint* endpoint)
     : endpoint_(endpoint), is_blocked_(false) {}
 
diff --git a/quic/test_tools/simulator/quic_endpoint.h b/quic/test_tools/simulator/quic_endpoint.h
index 53bff3a..c547551 100644
--- a/quic/test_tools/simulator/quic_endpoint.h
+++ b/quic/test_tools/simulator/quic_endpoint.h
@@ -130,6 +130,7 @@
                         TransmissionType type) override;
   bool IsFrameOutstanding(const QuicFrame& frame) const override;
   bool HasUnackedCryptoData() const override;
+  bool HasUnackedStreamData() const override;
   // End SessionNotifierInterface implementation.
 
  private: