Add experimental logic for RetransmittableOnWireTimeout
This change allows RetransmittableOnWireTimeout be set based on a multiple of PTO.
This new functionality is enabled only on client side and behind three tags (ROW1, ROW2, and ROW3), where:
- ROW1 will make the timeout value be equal to 1PTO
- ROW2 will make the timeout value be equal to 2PTO
- ROW3 will make the timeout value be equal to 3PTO.
This new logic is not enabled by default.
Protected by experimental quic connection options.
PiperOrigin-RevId: 775783151
diff --git a/quiche/quic/core/crypto/crypto_protocol.h b/quiche/quic/core/crypto/crypto_protocol.h
index 9056c47..9d1dbe2 100644
--- a/quiche/quic/core/crypto/crypto_protocol.h
+++ b/quiche/quic/core/crypto/crypto_protocol.h
@@ -445,6 +445,13 @@
// ROWP timeout.
DEFINE_STATIC_QUIC_TAG(ROWR); // Send random bytes on ROWP
// timeout.
+
+// Retransmittable on wire timeout experiment.
+// TODO: b/427246911 - Remove these tags once the experiment is complete.
+DEFINE_STATIC_QUIC_TAG(ROW1); // Set retransmittable on wire timeout to 1*PTO.
+DEFINE_STATIC_QUIC_TAG(ROW2); // Set retransmittable on wire timeout to 2*PTO.
+DEFINE_STATIC_QUIC_TAG(ROW3); // Set retransmittable on wire timeout to 3*PTO.
+
// Selective Resumption variants.
DEFINE_STATIC_QUIC_TAG(GSR0);
DEFINE_STATIC_QUIC_TAG(GSR1);
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc
index 1e6279e..fe95424 100644
--- a/quiche/quic/core/quic_connection.cc
+++ b/quiche/quic/core/quic_connection.cc
@@ -549,6 +549,20 @@
retransmittable_on_wire_behavior_ = SEND_RANDOM_BYTES;
}
}
+
+ // Set retransmittable-on-wire timeout to different PTO based values.
+ if (perspective_ == Perspective::IS_CLIENT && version().HasIetfQuicFrames()) {
+ if (config.HasClientRequestedIndependentOption(kROW1, perspective_)) {
+ ping_manager_.set_num_ptos_for_retransmittable_on_wire_timeout(1);
+ }
+ if (config.HasClientRequestedIndependentOption(kROW2, perspective_)) {
+ ping_manager_.set_num_ptos_for_retransmittable_on_wire_timeout(2);
+ }
+ if (config.HasClientRequestedIndependentOption(kROW3, perspective_)) {
+ ping_manager_.set_num_ptos_for_retransmittable_on_wire_timeout(3);
+ }
+ }
+
if (config.HasClientRequestedIndependentOption(k3AFF, perspective_)) {
anti_amplification_factor_ = 3;
}
@@ -4892,7 +4906,8 @@
}
ping_manager_.SetAlarm(clock_->ApproximateNow(),
visitor_->ShouldKeepConnectionAlive(),
- sent_packet_manager_.HasInFlightPackets());
+ sent_packet_manager_.HasInFlightPackets(),
+ sent_packet_manager_.GetPtoDelay());
}
void QuicConnection::SetRetransmissionAlarm() {
@@ -6580,9 +6595,9 @@
if (connected_) {
// Always reset PING alarm with has_in_flight_packets=true. This is used
// to avoid re-arming the alarm in retransmittable-on-wire mode.
- ping_manager_.SetAlarm(clock_->ApproximateNow(),
- visitor_->ShouldKeepConnectionAlive(),
- /*has_in_flight_packets=*/true);
+ ping_manager_.SetAlarm(
+ clock_->ApproximateNow(), visitor_->ShouldKeepConnectionAlive(),
+ /*has_in_flight_packets=*/true, sent_packet_manager_.GetPtoDelay());
}
return;
}
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc
index c0251ab..74060c0 100644
--- a/quiche/quic/core/quic_connection_test.cc
+++ b/quiche/quic/core/quic_connection_test.cc
@@ -8587,6 +8587,40 @@
connection_.GetPingAlarm()->deadline() - clock_.ApproximateNow());
}
+// Make sure when enabled, the retransmittable on wire timeout is based on the
+// PTO.
+TEST_P(QuicConnectionTest, PtoBasedRetransmittableOnWireTimeout) {
+ if (!VersionHasIetfQuicFrames(connection_.version().transport_version)) {
+ return;
+ }
+
+ EXPECT_CALL(*send_algorithm_, EnableECT1()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*send_algorithm_, EnableECT0()).WillRepeatedly(Return(false));
+
+ // Enable the retransmittable on wire timeout for 3 different PTOs.
+ struct TestCase {
+ QuicTag timeout_tag;
+ uint8_t expected_pto_count;
+ };
+ static constexpr TestCase kTestCases[] = {
+ {kROW1, 1},
+ {kROW2, 2},
+ {kROW3, 3},
+ };
+
+ for (const auto& test_case : kTestCases) {
+ QuicConfig config;
+ QuicTagVector connection_options;
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ connection_options.push_back(test_case.timeout_tag);
+ config.SetClientConnectionOptions(connection_options);
+ connection_.SetFromConfig(config);
+ EXPECT_EQ(QuicConnectionPeer::GetNumPtosForRetransmittableOnWireTimeout(
+ &connection_),
+ test_case.expected_pto_count);
+ }
+}
+
TEST_P(QuicConnectionTest, ValidStatelessResetToken) {
const StatelessResetToken kTestToken{0, 1, 0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1, 0, 1};
diff --git a/quiche/quic/core/quic_ping_manager.cc b/quiche/quic/core/quic_ping_manager.cc
index 533dd10..52a5cd4 100644
--- a/quiche/quic/core/quic_ping_manager.cc
+++ b/quiche/quic/core/quic_ping_manager.cc
@@ -25,8 +25,9 @@
: perspective_(perspective), delegate_(delegate), alarm_(alarm) {}
void QuicPingManager::SetAlarm(QuicTime now, bool should_keep_alive,
- bool has_in_flight_packets) {
- UpdateDeadlines(now, should_keep_alive, has_in_flight_packets);
+ bool has_in_flight_packets,
+ QuicTime::Delta pto_delay) {
+ UpdateDeadlines(now, should_keep_alive, has_in_flight_packets, pto_delay);
const QuicTime earliest_deadline = GetEarliestDeadline();
if (!earliest_deadline.IsInitialized()) {
alarm_.Cancel();
@@ -72,7 +73,8 @@
}
void QuicPingManager::UpdateDeadlines(QuicTime now, bool should_keep_alive,
- bool has_in_flight_packets) {
+ bool has_in_flight_packets,
+ QuicTime::Delta pto_delay) {
// Reset keep-alive deadline given it will be set later (with left edge
// |now|).
keep_alive_deadline_ = QuicTime::Zero();
@@ -96,7 +98,8 @@
// Clients send 15s PINGs to avoid NATs from timing out.
keep_alive_deadline_ = now + keep_alive_timeout_;
}
- if (initial_retransmittable_on_wire_timeout_.IsInfinite() ||
+ if ((num_ptos_for_retransmittable_on_wire_timeout_ == 0 &&
+ initial_retransmittable_on_wire_timeout_.IsInfinite()) ||
has_in_flight_packets ||
retransmittable_on_wire_count_ >
GetQuicFlag(quic_max_retransmittable_on_wire_ping_count)) {
@@ -105,10 +108,18 @@
return;
}
- QUICHE_DCHECK_LT(initial_retransmittable_on_wire_timeout_,
- keep_alive_timeout_);
- QuicTime::Delta retransmittable_on_wire_timeout =
- initial_retransmittable_on_wire_timeout_;
+ QuicTime::Delta retransmittable_on_wire_timeout = QuicTime::Delta::Zero();
+ if (num_ptos_for_retransmittable_on_wire_timeout_ > 0) {
+ QUICHE_DCHECK_NE(pto_delay, QuicTime::Delta::Zero());
+ retransmittable_on_wire_timeout =
+ static_cast<int>(num_ptos_for_retransmittable_on_wire_timeout_) *
+ pto_delay;
+ } else {
+ QUICHE_DCHECK_LT(initial_retransmittable_on_wire_timeout_,
+ keep_alive_timeout_);
+ retransmittable_on_wire_timeout = initial_retransmittable_on_wire_timeout_;
+ }
+
const int max_aggressive_retransmittable_on_wire_count =
GetQuicFlag(quic_max_aggressive_retransmittable_on_wire_ping_count);
QUICHE_DCHECK_LE(0, max_aggressive_retransmittable_on_wire_count);
@@ -120,8 +131,9 @@
max_aggressive_retransmittable_on_wire_count,
kMaxRetransmittableOnWireDelayShift);
retransmittable_on_wire_timeout =
- initial_retransmittable_on_wire_timeout_ * (1 << shift);
+ retransmittable_on_wire_timeout * (1 << shift);
}
+
if (retransmittable_on_wire_deadline_.IsInitialized() &&
retransmittable_on_wire_deadline_ <
now + retransmittable_on_wire_timeout) {
diff --git a/quiche/quic/core/quic_ping_manager.h b/quiche/quic/core/quic_ping_manager.h
index 3250416..4cf9810 100644
--- a/quiche/quic/core/quic_ping_manager.h
+++ b/quiche/quic/core/quic_ping_manager.h
@@ -5,6 +5,8 @@
#ifndef QUICHE_QUIC_CORE_QUIC_PING_MANAGER_H_
#define QUICHE_QUIC_CORE_QUIC_PING_MANAGER_H_
+#include <cstdint>
+
#include "quiche/quic/core/quic_alarm.h"
#include "quiche/quic/core/quic_alarm_factory.h"
#include "quiche/quic/core/quic_connection_alarms.h"
@@ -43,7 +45,7 @@
// Called to set |alarm_|.
void SetAlarm(QuicTime now, bool should_keep_alive,
- bool has_in_flight_packets);
+ bool has_in_flight_packets, QuicTime::Delta pto_delay);
// Called when |alarm_| fires.
void OnAlarm();
@@ -66,13 +68,19 @@
consecutive_retransmittable_on_wire_count_ = 0;
}
+ void set_num_ptos_for_retransmittable_on_wire_timeout(
+ uint8_t num_ptos_for_retransmittable_on_wire_timeout) {
+ num_ptos_for_retransmittable_on_wire_timeout_ =
+ num_ptos_for_retransmittable_on_wire_timeout;
+ }
+
private:
friend class test::QuicConnectionPeer;
friend class test::QuicPingManagerPeer;
// Update |retransmittable_on_wire_deadline_| and |keep_alive_deadline_|.
void UpdateDeadlines(QuicTime now, bool should_keep_alive,
- bool has_in_flight_packets);
+ bool has_in_flight_packets, QuicTime::Delta pto_delay);
// Get earliest deadline of |retransmittable_on_wire_deadline_| and
// |keep_alive_deadline_|. Returns 0 if both deadlines are not initialized.
@@ -101,6 +109,8 @@
QuicTime keep_alive_deadline_ = QuicTime::Zero();
QuicAlarmProxy alarm_;
+
+ uint8_t num_ptos_for_retransmittable_on_wire_timeout_ = 0;
};
} // namespace quic
diff --git a/quiche/quic/core/quic_ping_manager_test.cc b/quiche/quic/core/quic_ping_manager_test.cc
index e2a3155..0ab3331 100644
--- a/quiche/quic/core/quic_ping_manager_test.cc
+++ b/quiche/quic/core/quic_ping_manager_test.cc
@@ -26,6 +26,7 @@
const bool kShouldKeepAlive = true;
const bool kHasInflightPackets = true;
+const QuicTime::Delta kPtoDelay = QuicTime::Delta::FromMilliseconds(50);
class MockDelegate : public QuicPingManager::Delegate {
public:
@@ -62,7 +63,7 @@
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
// Set alarm with in flight packets.
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- kHasInflightPackets);
+ kHasInflightPackets, kPtoDelay);
EXPECT_TRUE(alarm_->IsSet());
EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs),
alarm_->deadline() - clock_.ApproximateNow());
@@ -70,7 +71,7 @@
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
// Reset alarm with no in flight packets.
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- !kHasInflightPackets);
+ !kHasInflightPackets, kPtoDelay);
EXPECT_TRUE(alarm_->IsSet());
// Verify the deadline is set slightly less than 15 seconds in the future,
// because of the 1s alarm granularity.
@@ -84,13 +85,13 @@
EXPECT_FALSE(alarm_->IsSet());
// Reset alarm with in flight packets.
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- kHasInflightPackets);
+ kHasInflightPackets, kPtoDelay);
EXPECT_TRUE(alarm_->IsSet());
// Verify alarm is not armed if !kShouldKeepAlive.
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
manager_.SetAlarm(clock_.ApproximateNow(), !kShouldKeepAlive,
- kHasInflightPackets);
+ kHasInflightPackets, kPtoDelay);
EXPECT_FALSE(alarm_->IsSet());
}
@@ -103,7 +104,7 @@
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
// Set alarm with in flight packets.
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- kHasInflightPackets);
+ kHasInflightPackets, kPtoDelay);
EXPECT_TRUE(alarm_->IsSet());
EXPECT_EQ(QuicTime::Delta::FromSeconds(10),
alarm_->deadline() - clock_.ApproximateNow());
@@ -111,7 +112,7 @@
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
// Set alarm with no in flight packets.
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- !kHasInflightPackets);
+ !kHasInflightPackets, kPtoDelay);
EXPECT_TRUE(alarm_->IsSet());
// The deadline is set slightly less than 10 seconds in the future, because
// of the 1s alarm granularity.
@@ -125,13 +126,13 @@
EXPECT_FALSE(alarm_->IsSet());
// Reset alarm with in flight packets.
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- kHasInflightPackets);
+ kHasInflightPackets, kPtoDelay);
EXPECT_TRUE(alarm_->IsSet());
// Verify alarm is not armed if !kShouldKeepAlive.
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
manager_.SetAlarm(clock_.ApproximateNow(), !kShouldKeepAlive,
- kHasInflightPackets);
+ kHasInflightPackets, kPtoDelay);
EXPECT_FALSE(alarm_->IsSet());
}
@@ -145,7 +146,7 @@
// Set alarm with in flight packets.
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- kHasInflightPackets);
+ kHasInflightPackets, kPtoDelay);
// Verify alarm is in keep-alive mode.
EXPECT_TRUE(alarm_->IsSet());
EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs),
@@ -154,7 +155,7 @@
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
// Set alarm with no in flight packets.
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- !kHasInflightPackets);
+ !kHasInflightPackets, kPtoDelay);
EXPECT_TRUE(alarm_->IsSet());
// Verify alarm is in retransmittable-on-wire mode.
EXPECT_EQ(kRtransmittableOnWireTimeout,
@@ -166,7 +167,7 @@
EXPECT_FALSE(alarm_->IsSet());
// Reset alarm with in flight packets.
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- kHasInflightPackets);
+ kHasInflightPackets, kPtoDelay);
// Verify the alarm is in keep-alive mode.
ASSERT_TRUE(alarm_->IsSet());
EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs),
@@ -185,7 +186,7 @@
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
EXPECT_FALSE(alarm_->IsSet());
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- kHasInflightPackets);
+ kHasInflightPackets, kPtoDelay);
// Verify alarm is in keep-alive mode.
EXPECT_TRUE(alarm_->IsSet());
EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs),
@@ -197,7 +198,7 @@
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
// Reset alarm with no in flight packets.
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- !kHasInflightPackets);
+ !kHasInflightPackets, kPtoDelay);
EXPECT_TRUE(alarm_->IsSet());
// Verify alarm is in retransmittable-on-wire mode.
EXPECT_EQ(initial_retransmittable_on_wire_timeout,
@@ -208,7 +209,7 @@
EXPECT_FALSE(alarm_->IsSet());
// Reset alarm with in flight packets.
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- kHasInflightPackets);
+ kHasInflightPackets, kPtoDelay);
}
QuicTime::Delta retransmittable_on_wire_timeout =
@@ -221,7 +222,7 @@
retransmittable_on_wire_timeout = retransmittable_on_wire_timeout * 2;
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- !kHasInflightPackets);
+ !kHasInflightPackets, kPtoDelay);
EXPECT_TRUE(alarm_->IsSet());
EXPECT_EQ(retransmittable_on_wire_timeout,
alarm_->deadline() - clock_.ApproximateNow());
@@ -232,7 +233,7 @@
EXPECT_FALSE(alarm_->IsSet());
// Reset alarm with in flight packets.
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- kHasInflightPackets);
+ kHasInflightPackets, kPtoDelay);
}
// Verify alarm is in keep-alive mode.
@@ -243,7 +244,7 @@
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
// Reset alarm with no in flight packets
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- !kHasInflightPackets);
+ !kHasInflightPackets, kPtoDelay);
EXPECT_TRUE(alarm_->IsSet());
// Verify alarm is in keep-alive mode because retransmittable-on-wire deadline
// is later.
@@ -270,7 +271,7 @@
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
EXPECT_FALSE(alarm_->IsSet());
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- kHasInflightPackets);
+ kHasInflightPackets, kPtoDelay);
// Verify alarm is in keep-alive mode.
EXPECT_TRUE(alarm_->IsSet());
EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs),
@@ -278,7 +279,7 @@
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- !kHasInflightPackets);
+ !kHasInflightPackets, kPtoDelay);
EXPECT_TRUE(alarm_->IsSet());
// Verify alarm is in retransmittable-on-wire mode.
EXPECT_EQ(initial_retransmittable_on_wire_timeout,
@@ -290,14 +291,14 @@
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- !kHasInflightPackets);
+ !kHasInflightPackets, kPtoDelay);
EXPECT_TRUE(alarm_->IsSet());
EXPECT_EQ(initial_retransmittable_on_wire_timeout,
alarm_->deadline() - clock_.ApproximateNow());
manager_.reset_consecutive_retransmittable_on_wire_count();
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- !kHasInflightPackets);
+ !kHasInflightPackets, kPtoDelay);
EXPECT_EQ(initial_retransmittable_on_wire_timeout,
alarm_->deadline() - clock_.ApproximateNow());
EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout());
@@ -306,7 +307,7 @@
for (int i = 0; i < kMaxAggressiveRetransmittableOnWireCount; i++) {
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- !kHasInflightPackets);
+ !kHasInflightPackets, kPtoDelay);
EXPECT_TRUE(alarm_->IsSet());
EXPECT_EQ(initial_retransmittable_on_wire_timeout,
alarm_->deadline() - clock_.ApproximateNow());
@@ -315,13 +316,13 @@
alarm_->Fire();
// Reset alarm with in flight packets.
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- kHasInflightPackets);
+ kHasInflightPackets, kPtoDelay);
// Advance 5ms to receive next packet.
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
}
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- !kHasInflightPackets);
+ !kHasInflightPackets, kPtoDelay);
EXPECT_TRUE(alarm_->IsSet());
EXPECT_EQ(initial_retransmittable_on_wire_timeout * 2,
alarm_->deadline() - clock_.ApproximateNow());
@@ -333,7 +334,7 @@
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
manager_.reset_consecutive_retransmittable_on_wire_count();
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- !kHasInflightPackets);
+ !kHasInflightPackets, kPtoDelay);
EXPECT_TRUE(alarm_->IsSet());
EXPECT_EQ(initial_retransmittable_on_wire_timeout,
alarm_->deadline() - clock_.ApproximateNow());
@@ -354,7 +355,7 @@
clock_.AdvanceTime(kShortDelay);
EXPECT_FALSE(alarm_->IsSet());
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- kHasInflightPackets);
+ kHasInflightPackets, kPtoDelay);
EXPECT_TRUE(alarm_->IsSet());
EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs),
@@ -363,7 +364,7 @@
for (int i = 0; i <= kMaxRetransmittableOnWirePingCount; i++) {
clock_.AdvanceTime(kShortDelay);
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- !kHasInflightPackets);
+ !kHasInflightPackets, kPtoDelay);
EXPECT_TRUE(alarm_->IsSet());
EXPECT_EQ(initial_retransmittable_on_wire_timeout,
alarm_->deadline() - clock_.ApproximateNow());
@@ -371,11 +372,11 @@
EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout());
alarm_->Fire();
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- kHasInflightPackets);
+ kHasInflightPackets, kPtoDelay);
}
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- !kHasInflightPackets);
+ !kHasInflightPackets, kPtoDelay);
EXPECT_TRUE(alarm_->IsSet());
// Verify alarm is in keep-alive mode.
EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs),
@@ -398,7 +399,7 @@
for (int i = 0; i <= kMaxAggressiveRetransmittableOnWireCount; i++) {
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- !kHasInflightPackets);
+ !kHasInflightPackets, kPtoDelay);
EXPECT_TRUE(alarm_->IsSet());
EXPECT_EQ(initial_retransmittable_on_wire_timeout,
alarm_->deadline() - clock_.ApproximateNow());
@@ -406,11 +407,11 @@
EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout());
alarm_->Fire();
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- kHasInflightPackets);
+ kHasInflightPackets, kPtoDelay);
}
for (int i = 1; i <= 20; ++i) {
manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
- !kHasInflightPackets);
+ !kHasInflightPackets, kPtoDelay);
EXPECT_TRUE(alarm_->IsSet());
if (i <= 10) {
EXPECT_EQ(initial_retransmittable_on_wire_timeout * (1 << i),
@@ -426,6 +427,113 @@
}
}
+TEST_F(QuicPingManagerTest, PtoBasedRetransmittableOnWireTimeout) {
+ // Set the initial retransmittable on wire timeout.
+ manager_.set_initial_retransmittable_on_wire_timeout(
+ QuicTime::Delta::FromMilliseconds(200));
+ // Verify the alarm is set based on different value based on PTO delay.
+ for (int num_times_pto : {1, 2, 3}) {
+ EXPECT_TRUE(!alarm_->IsSet());
+ manager_.set_num_ptos_for_retransmittable_on_wire_timeout(num_times_pto);
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ !kHasInflightPackets, kPtoDelay);
+ EXPECT_EQ(kPtoDelay * num_times_pto,
+ alarm_->deadline() - clock_.ApproximateNow());
+ EXPECT_TRUE(alarm_->IsSet());
+ clock_.AdvanceTime(kPtoDelay * num_times_pto);
+ EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout());
+ alarm_->Fire();
+ }
+}
+
+TEST_F(QuicPingManagerTest,
+ PtoBasedRetransmittableOnWireTimeoutExponentiallyBackOff) {
+ const int kMaxAggressiveRetransmittableOnWireCount = 5;
+ SetQuicFlag(quic_max_aggressive_retransmittable_on_wire_ping_count,
+ kMaxAggressiveRetransmittableOnWireCount);
+
+ for (int num_times_pto : {1, 2, 3}) {
+ manager_.set_num_ptos_for_retransmittable_on_wire_timeout(num_times_pto);
+
+ QuicTime::Delta pto_delay = static_cast<int>(num_times_pto) * kPtoDelay;
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_FALSE(alarm_->IsSet());
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ kHasInflightPackets, kPtoDelay);
+ // Verify alarm is in keep-alive mode.
+ EXPECT_TRUE(alarm_->IsSet());
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs),
+ alarm_->deadline() - clock_.ApproximateNow());
+
+ // Verify no exponential backoff on the first few retransmittable on wire
+ // timeouts.
+ for (int i = 0; i <= kMaxAggressiveRetransmittableOnWireCount; ++i) {
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ // Reset alarm with no in flight packets.
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ !kHasInflightPackets, kPtoDelay);
+ EXPECT_TRUE(alarm_->IsSet());
+ // Verify alarm is in retransmittable-on-wire mode.
+ EXPECT_EQ(pto_delay, alarm_->deadline() - clock_.ApproximateNow());
+ clock_.AdvanceTime(pto_delay);
+ EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout());
+ alarm_->Fire();
+ EXPECT_FALSE(alarm_->IsSet());
+ // Reset alarm with in flight packets.
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ kHasInflightPackets, kPtoDelay);
+ }
+
+ QuicTime::Delta retransmittable_on_wire_timeout = pto_delay;
+
+ // Verify subsequent retransmittable-on-wire timeout is exponentially
+ // backed off.
+ while (retransmittable_on_wire_timeout * 2 <
+ QuicTime::Delta::FromSeconds(kPingTimeoutSecs)) {
+ retransmittable_on_wire_timeout = retransmittable_on_wire_timeout * 2;
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ !kHasInflightPackets, kPtoDelay);
+ EXPECT_TRUE(alarm_->IsSet());
+ EXPECT_EQ(retransmittable_on_wire_timeout,
+ alarm_->deadline() - clock_.ApproximateNow());
+
+ clock_.AdvanceTime(retransmittable_on_wire_timeout);
+ EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout());
+ alarm_->Fire();
+ EXPECT_FALSE(alarm_->IsSet());
+ // Reset alarm with in flight packets.
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ kHasInflightPackets, kPtoDelay);
+ }
+
+ // Verify alarm is in keep-alive mode.
+ EXPECT_TRUE(alarm_->IsSet());
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs),
+ alarm_->deadline() - clock_.ApproximateNow());
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ // Reset alarm with no in flight packets
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ !kHasInflightPackets, kPtoDelay);
+ EXPECT_TRUE(alarm_->IsSet());
+ // Verify alarm is in keep-alive mode because retransmittable-on-wire
+ // deadline is later.
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs) -
+ QuicTime::Delta::FromMilliseconds(5),
+ alarm_->deadline() - clock_.ApproximateNow());
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(kPingTimeoutSecs) -
+ QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_CALL(delegate_, OnKeepAliveTimeout());
+ alarm_->Fire();
+ EXPECT_FALSE(alarm_->IsSet());
+
+ // Reset the consecutive retransmittable on wire count for next iteration.
+ manager_.reset_consecutive_retransmittable_on_wire_count();
+ }
+}
+
} // namespace
} // namespace test
diff --git a/quiche/quic/test_tools/quic_connection_peer.cc b/quiche/quic/test_tools/quic_connection_peer.cc
index 001e690..c0d713a 100644
--- a/quiche/quic/test_tools/quic_connection_peer.cc
+++ b/quiche/quic/test_tools/quic_connection_peer.cc
@@ -607,5 +607,12 @@
return connection->can_receive_ack_frequency_immediate_ack_;
}
+// static
+uint8_t QuicConnectionPeer::GetNumPtosForRetransmittableOnWireTimeout(
+ const QuicConnection* connection) {
+ return connection->ping_manager_
+ .num_ptos_for_retransmittable_on_wire_timeout_;
+}
+
} // namespace test
} // namespace quic
diff --git a/quiche/quic/test_tools/quic_connection_peer.h b/quiche/quic/test_tools/quic_connection_peer.h
index 06d8c97..c1477a8 100644
--- a/quiche/quic/test_tools/quic_connection_peer.h
+++ b/quiche/quic/test_tools/quic_connection_peer.h
@@ -253,6 +253,9 @@
static void OnForwardProgressMade(QuicConnection* connection);
static bool CanReceiveAckFrequencyFrames(QuicConnection* connection);
+
+ static uint8_t GetNumPtosForRetransmittableOnWireTimeout(
+ const QuicConnection* connection);
};
} // namespace test