Add QuicPingManager to manage the PING alarm.
Protected by FLAGS_quic_reloadable_flag_quic_use_ping_manager.
PiperOrigin-RevId: 442893828
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc
index 44a7830..82b07cc 100644
--- a/quiche/quic/core/quic_connection.cc
+++ b/quiche/quic/core/quic_connection.cc
@@ -128,6 +128,7 @@
void OnAlarm() override {
QUICHE_DCHECK(connection_->connected());
+ QUICHE_DCHECK(!GetQuicReloadableFlag(quic_use_ping_manager));
connection_->OnPingTimeout();
}
};
@@ -327,7 +328,8 @@
alarm_factory_, &context_),
path_validator_(alarm_factory_, &arena_, this, random_generator_,
&context_),
- most_recent_frame_type_(NUM_FRAME_TYPES) {
+ most_recent_frame_type_(NUM_FRAME_TYPES),
+ ping_manager_(perspective, this, &arena_, alarm_factory_, &context_) {
QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT ||
default_path_.self_address.IsInitialized());
@@ -1350,7 +1352,11 @@
MaybeUpdateAckTimeout();
visitor_->OnStreamFrame(frame);
stats_.stream_bytes_received += frame.data_length;
- consecutive_retransmittable_on_wire_ping_count_ = 0;
+ if (use_ping_manager_) {
+ ping_manager_.reset_consecutive_retransmittable_on_wire_count();
+ } else {
+ consecutive_retransmittable_on_wire_ping_count_ = 0;
+ }
return connected_;
}
@@ -4053,6 +4059,7 @@
}
void QuicConnection::OnPingTimeout() {
+ QUICHE_DCHECK(!use_ping_manager_);
if (retransmission_alarm_->IsSet() ||
!visitor_->ShouldKeepConnectionAlive()) {
return;
@@ -4621,7 +4628,11 @@
QUIC_DVLOG(1) << "Cancelling all QuicConnection alarms.";
ack_alarm_->PermanentCancel();
- ping_alarm_->PermanentCancel();
+ if (use_ping_manager_) {
+ ping_manager_.Stop();
+ } else {
+ ping_alarm_->PermanentCancel();
+ }
retransmission_alarm_->PermanentCancel();
send_alarm_->PermanentCancel();
mtu_discovery_alarm_->PermanentCancel();
@@ -4665,6 +4676,13 @@
if (!connected_) {
return;
}
+ if (use_ping_manager_) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_use_ping_manager);
+ ping_manager_.SetAlarm(clock_->ApproximateNow(),
+ visitor_->ShouldKeepConnectionAlive(),
+ sent_packet_manager_.HasInFlightPackets());
+ return;
+ }
if (perspective_ == Perspective::IS_SERVER &&
initial_retransmittable_on_wire_timeout_.IsInfinite()) {
// The PING alarm exists to support two features:
@@ -6280,6 +6298,24 @@
idle_timeout_connection_close_behavior_);
}
+void QuicConnection::OnKeepAliveTimeout() {
+ QUICHE_DCHECK(use_ping_manager_);
+ if (retransmission_alarm_->IsSet() ||
+ !visitor_->ShouldKeepConnectionAlive()) {
+ return;
+ }
+ SendPingAtLevel(framer().GetEncryptionLevelToSendApplicationData());
+}
+
+void QuicConnection::OnRetransmittableOnWireTimeout() {
+ QUICHE_DCHECK(use_ping_manager_);
+ if (retransmission_alarm_->IsSet() ||
+ !visitor_->ShouldKeepConnectionAlive()) {
+ return;
+ }
+ SendPingAtLevel(framer().GetEncryptionLevelToSendApplicationData());
+}
+
void QuicConnection::OnPeerIssuedConnectionIdRetired() {
QUICHE_DCHECK(peer_issued_cid_manager_ != nullptr);
QuicConnectionId* default_path_cid =
@@ -7107,5 +7143,26 @@
return old_send_algorithm;
}
+void QuicConnection::set_keep_alive_ping_timeout(
+ QuicTime::Delta keep_alive_ping_timeout) {
+ if (use_ping_manager_) {
+ ping_manager_.set_keep_alive_timeout(keep_alive_ping_timeout);
+ return;
+ }
+ QUICHE_DCHECK(!ping_alarm_->IsSet());
+ keep_alive_ping_timeout_ = keep_alive_ping_timeout;
+}
+
+void QuicConnection::set_initial_retransmittable_on_wire_timeout(
+ QuicTime::Delta retransmittable_on_wire_timeout) {
+ if (use_ping_manager_) {
+ ping_manager_.set_initial_retransmittable_on_wire_timeout(
+ retransmittable_on_wire_timeout);
+ return;
+ }
+ QUICHE_DCHECK(!ping_alarm_->IsSet());
+ initial_retransmittable_on_wire_timeout_ = retransmittable_on_wire_timeout;
+}
+
#undef ENDPOINT // undef for jumbo builds
} // namespace quic
diff --git a/quiche/quic/core/quic_connection.h b/quiche/quic/core/quic_connection.h
index 6aca00b..9c93dd4 100644
--- a/quiche/quic/core/quic_connection.h
+++ b/quiche/quic/core/quic_connection.h
@@ -50,6 +50,7 @@
#include "quiche/quic/core/quic_packet_writer.h"
#include "quiche/quic/core/quic_packets.h"
#include "quiche/quic/core/quic_path_validator.h"
+#include "quiche/quic/core/quic_ping_manager.h"
#include "quiche/quic/core/quic_sent_packet_manager.h"
#include "quiche/quic/core/quic_time.h"
#include "quiche/quic/core/quic_types.h"
@@ -458,7 +459,8 @@
public QuicNetworkBlackholeDetector::Delegate,
public QuicIdleNetworkDetector::Delegate,
public QuicPathValidator::SendDelegate,
- public QuicConnectionIdManagerVisitorInterface {
+ public QuicConnectionIdManagerVisitorInterface,
+ public QuicPingManager::Delegate {
public:
// Constructs a new QuicConnection for |connection_id| and
// |initial_peer_address| using |writer| to write packets. |owns_writer|
@@ -710,6 +712,10 @@
void OnHandshakeTimeout() override;
void OnIdleNetworkDetected() override;
+ // QuicPingManager::Delegate
+ void OnKeepAliveTimeout() override;
+ void OnRetransmittableOnWireTimeout() override;
+
// QuicConnectionIdManagerVisitorInterface
void OnPeerIssuedConnectionIdRetired() override;
bool SendNewConnectionId(const QuicNewConnectionIdFrame& frame) override;
@@ -741,17 +747,11 @@
}
// Used in Chromium, but not internally.
// Must only be called before ping_alarm_ is set.
- void set_keep_alive_ping_timeout(QuicTime::Delta keep_alive_ping_timeout) {
- QUICHE_DCHECK(!ping_alarm_->IsSet());
- keep_alive_ping_timeout_ = keep_alive_ping_timeout;
- }
+ void set_keep_alive_ping_timeout(QuicTime::Delta keep_alive_ping_timeout);
// Sets an initial timeout for the ping alarm when there is no retransmittable
// data in flight, allowing for a more aggressive ping alarm in that case.
void set_initial_retransmittable_on_wire_timeout(
- QuicTime::Delta retransmittable_on_wire_timeout) {
- QUICHE_DCHECK(!ping_alarm_->IsSet());
- initial_retransmittable_on_wire_timeout_ = retransmittable_on_wire_timeout;
- }
+ QuicTime::Delta retransmittable_on_wire_timeout);
// Used in Chromium, but not internally.
void set_creator_debug_delegate(QuicPacketCreator::DebugDelegate* visitor) {
packet_creator_.set_debug_delegate(visitor);
@@ -1976,6 +1976,8 @@
// SendAlarm.
bool defer_send_in_response_to_packets_;
+ // TODO(fayang): remove PING related fields below when deprecating
+ // quic_use_ping_manager.
// The timeout for keep-alive PING.
QuicTime::Delta keep_alive_ping_timeout_;
@@ -1999,6 +2001,7 @@
// An alarm that is scheduled when the SentPacketManager requires a delay
// before sending packets and fires when the packet may be sent.
QuicArenaScopedPtr<QuicAlarm> send_alarm_;
+ // TODO(fayang): remove ping_alarm_ when deprecating quic_use_ping_manager.
// An alarm that fires when a ping should be sent.
QuicArenaScopedPtr<QuicAlarm> ping_alarm_;
// An alarm that fires when an MTU probe should be sent.
@@ -2247,6 +2250,10 @@
// If true, send connection close packet on INVALID_VERSION.
bool send_connection_close_for_invalid_version_ = false;
+ const bool use_ping_manager_ = GetQuicReloadableFlag(quic_use_ping_manager);
+
+ QuicPingManager ping_manager_;
+
// TODO(b/205023946) Debug-only fields, to be deprecated after the bug is
// fixed.
absl::optional<QuicWallTime> quic_bug_10511_43_timestamp_;
diff --git a/quiche/quic/core/quic_flags_list.h b/quiche/quic/core/quic_flags_list.h
index 085f9d3..bfc2462 100644
--- a/quiche/quic/core/quic_flags_list.h
+++ b/quiche/quic/core/quic_flags_list.h
@@ -93,6 +93,8 @@
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_update_ack_timeout_on_receipt_time, true)
// If true, use BBRv2 as the default congestion controller. Takes precedence over --quic_default_to_bbr.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_to_bbr_v2, false)
+// If true, use PING manager to manage the PING alarm.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_use_ping_manager, true)
// If true, use new connection ID in connection migration.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_connection_migration_use_new_cid_v2, true)
// If true, uses conservative cwnd gain and pacing gain when cwnd gets bootstrapped.
diff --git a/quiche/quic/core/quic_one_block_arena.h b/quiche/quic/core/quic_one_block_arena.h
index 4bebebe..05d335b 100644
--- a/quiche/quic/core/quic_one_block_arena.h
+++ b/quiche/quic/core/quic_one_block_arena.h
@@ -69,7 +69,7 @@
// QuicConnections currently use around 1KB of polymorphic types which would
// ordinarily be on the heap. Instead, store them inline in an arena.
-using QuicConnectionArena = QuicOneBlockArena<1152>;
+using QuicConnectionArena = QuicOneBlockArena<1248>;
} // namespace quic
diff --git a/quiche/quic/core/quic_ping_manager.cc b/quiche/quic/core/quic_ping_manager.cc
new file mode 100644
index 0000000..7ce158d
--- /dev/null
+++ b/quiche/quic/core/quic_ping_manager.cc
@@ -0,0 +1,158 @@
+// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "quiche/quic/core/quic_ping_manager.h"
+
+namespace quic {
+
+namespace {
+
+class AlarmDelegate : public QuicAlarm::DelegateWithContext {
+ public:
+ explicit AlarmDelegate(QuicPingManager* manager,
+ QuicConnectionContext* context)
+ : QuicAlarm::DelegateWithContext(context), manager_(manager) {}
+ AlarmDelegate(const AlarmDelegate&) = delete;
+ AlarmDelegate& operator=(const AlarmDelegate&) = delete;
+
+ void OnAlarm() override { manager_->OnAlarm(); }
+
+ private:
+ QuicPingManager* manager_;
+};
+
+} // namespace
+
+QuicPingManager::QuicPingManager(Perspective perspective, Delegate* delegate,
+ QuicConnectionArena* arena,
+ QuicAlarmFactory* alarm_factory,
+ QuicConnectionContext* context)
+ : perspective_(perspective),
+ delegate_(delegate),
+ alarm_(alarm_factory->CreateAlarm(
+ arena->New<AlarmDelegate>(this, context), arena)) {}
+
+void QuicPingManager::SetAlarm(QuicTime now, bool should_keep_alive,
+ bool has_in_flight_packets) {
+ UpdateDeadlines(now, should_keep_alive, has_in_flight_packets);
+ const QuicTime earliest_deadline = GetEarliestDeadline();
+ if (!earliest_deadline.IsInitialized()) {
+ alarm_->Cancel();
+ return;
+ }
+ if (earliest_deadline == keep_alive_deadline_) {
+ // Use 1s granularity for keep-alive time.
+ alarm_->Update(earliest_deadline, QuicTime::Delta::FromSeconds(1));
+ return;
+ }
+ alarm_->Update(earliest_deadline, kAlarmGranularity);
+ if (GetQuicFlag(
+ FLAGS_quic_max_aggressive_retransmittable_on_wire_ping_count) != 0) {
+ ++consecutive_retransmittable_on_wire_count_;
+ }
+ ++retransmittable_on_wire_count_;
+}
+
+void QuicPingManager::OnAlarm() {
+ const QuicTime earliest_deadline = GetEarliestDeadline();
+ if (!earliest_deadline.IsInitialized()) {
+ QUIC_BUG(quic_ping_manager_alarm_fires_unexpectedly)
+ << "QuicPingManager alarm fires unexpectedly.";
+ return;
+ }
+ // Please note, alarm does not get re-armed here, and we are relying on caller
+ // to SetAlarm later.
+ if (earliest_deadline == retransmittable_on_wire_deadline_) {
+ retransmittable_on_wire_deadline_ = QuicTime::Zero();
+ delegate_->OnRetransmittableOnWireTimeout();
+ return;
+ }
+ if (earliest_deadline == keep_alive_deadline_) {
+ keep_alive_deadline_ = QuicTime::Zero();
+ delegate_->OnKeepAliveTimeout();
+ }
+}
+
+void QuicPingManager::Stop() {
+ alarm_->PermanentCancel();
+ retransmittable_on_wire_deadline_ = QuicTime::Zero();
+ keep_alive_deadline_ = QuicTime::Zero();
+}
+
+void QuicPingManager::UpdateDeadlines(QuicTime now, bool should_keep_alive,
+ bool has_in_flight_packets) {
+ // Reset keep-alive deadline given it will be set later (with left edge
+ // |now|).
+ keep_alive_deadline_ = QuicTime::Zero();
+ if (perspective_ == Perspective::IS_SERVER &&
+ initial_retransmittable_on_wire_timeout_.IsInfinite()) {
+ // The PING alarm exists to support two features:
+ // 1) clients send PINGs every 15s to prevent NAT timeouts,
+ // 2) both clients and servers can send retransmittable on the wire PINGs
+ // (ROWP) while ShouldKeepConnectionAlive is true and there is no packets in
+ // flight.
+ QUICHE_DCHECK(!retransmittable_on_wire_deadline_.IsInitialized());
+ return;
+ }
+ if (!should_keep_alive) {
+ // Don't send a ping unless the application (ie: HTTP/3) says to, usually
+ // because it is expecting a response from the peer.
+ retransmittable_on_wire_deadline_ = QuicTime::Zero();
+ return;
+ }
+ if (perspective_ == Perspective::IS_CLIENT) {
+ // Clients send 15s PINGs to avoid NATs from timing out.
+ keep_alive_deadline_ = now + keep_alive_timeout_;
+ }
+ if (initial_retransmittable_on_wire_timeout_.IsInfinite() ||
+ has_in_flight_packets ||
+ retransmittable_on_wire_count_ >
+ GetQuicFlag(FLAGS_quic_max_retransmittable_on_wire_ping_count)) {
+ // No need to set retransmittable-on-wire timeout.
+ retransmittable_on_wire_deadline_ = QuicTime::Zero();
+ return;
+ }
+
+ QUICHE_DCHECK_LT(initial_retransmittable_on_wire_timeout_,
+ keep_alive_timeout_);
+ QuicTime::Delta retransmittable_on_wire_timeout =
+ initial_retransmittable_on_wire_timeout_;
+ const int max_aggressive_retransmittable_on_wire_count =
+ GetQuicFlag(FLAGS_quic_max_aggressive_retransmittable_on_wire_ping_count);
+ QUICHE_DCHECK_LE(0, max_aggressive_retransmittable_on_wire_count);
+ if (consecutive_retransmittable_on_wire_count_ >
+ max_aggressive_retransmittable_on_wire_count) {
+ // Exponentially back off the timeout if the number of consecutive
+ // retransmittable on wire pings has exceeds the allowance.
+ int shift = consecutive_retransmittable_on_wire_count_ -
+ max_aggressive_retransmittable_on_wire_count;
+ retransmittable_on_wire_timeout =
+ initial_retransmittable_on_wire_timeout_ * (1 << shift);
+ }
+ if (retransmittable_on_wire_deadline_.IsInitialized() &&
+ retransmittable_on_wire_deadline_ <
+ now + retransmittable_on_wire_timeout) {
+ // Alarm is set to an earlier time. Do not postpone it.
+ QUIC_BUG_IF(quic_retransmittable_on_wire_deadline_is_in_past,
+ retransmittable_on_wire_deadline_ < now)
+ << "QUIC retransmittable PING deadline is in past";
+ return;
+ }
+ retransmittable_on_wire_deadline_ = now + retransmittable_on_wire_timeout;
+}
+
+QuicTime QuicPingManager::GetEarliestDeadline() const {
+ QuicTime earliest_deadline = QuicTime::Zero();
+ for (QuicTime t : {retransmittable_on_wire_deadline_, keep_alive_deadline_}) {
+ if (!t.IsInitialized()) {
+ continue;
+ }
+ if (!earliest_deadline.IsInitialized() || t < earliest_deadline) {
+ earliest_deadline = t;
+ }
+ }
+ return earliest_deadline;
+}
+
+} // namespace quic
diff --git a/quiche/quic/core/quic_ping_manager.h b/quiche/quic/core/quic_ping_manager.h
new file mode 100644
index 0000000..d88dac2
--- /dev/null
+++ b/quiche/quic/core/quic_ping_manager.h
@@ -0,0 +1,108 @@
+// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_PING_MANAGER_H_
+#define QUICHE_QUIC_CORE_QUIC_PING_MANAGER_H_
+
+#include "quiche/quic/core/quic_alarm.h"
+#include "quiche/quic/core/quic_alarm_factory.h"
+#include "quiche/quic/core/quic_constants.h"
+#include "quiche/quic/core/quic_one_block_arena.h"
+#include "quiche/quic/core/quic_time.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+namespace test {
+class QuicConnectionPeer;
+class QuicPingManagerPeer;
+} // namespace test
+
+// QuicPingManager manages an alarm that has two modes:
+// 1) keep-alive. When alarm fires, send packet to extend idle timeout to keep
+// connection alive.
+// 2) retransmittable-on-wire. When alarm fires, send packets to detect path
+// degrading (used in IP/port migrations).
+class QUIC_EXPORT_PRIVATE QuicPingManager {
+ public:
+ // Interface that get notified when |alarm_| fires.
+ class QUIC_EXPORT_PRIVATE Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ // Called when alarm fires in keep-alive mode.
+ virtual void OnKeepAliveTimeout() = 0;
+ // Called when alarm fires in retransmittable-on-wire mode.
+ virtual void OnRetransmittableOnWireTimeout() = 0;
+ };
+
+ QuicPingManager(Perspective perspective, Delegate* delegate,
+ QuicConnectionArena* arena, QuicAlarmFactory* alarm_factory,
+ QuicConnectionContext* context);
+
+ // Called to set |alarm_|.
+ void SetAlarm(QuicTime now, bool should_keep_alive,
+ bool has_in_flight_packets);
+
+ // Called when |alarm_| fires.
+ void OnAlarm();
+
+ // Called to stop |alarm_| permanently.
+ void Stop();
+
+ void set_keep_alive_timeout(QuicTime::Delta keep_alive_timeout) {
+ QUICHE_DCHECK(!alarm_->IsSet());
+ keep_alive_timeout_ = keep_alive_timeout;
+ }
+
+ void set_initial_retransmittable_on_wire_timeout(
+ QuicTime::Delta retransmittable_on_wire_timeout) {
+ QUICHE_DCHECK(!alarm_->IsSet());
+ initial_retransmittable_on_wire_timeout_ = retransmittable_on_wire_timeout;
+ }
+
+ void reset_consecutive_retransmittable_on_wire_count() {
+ consecutive_retransmittable_on_wire_count_ = 0;
+ }
+
+ 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);
+
+ // Get earliest deadline of |retransmittable_on_wire_deadline_| and
+ // |keep_alive_deadline_|. Returns 0 if both deadlines are not initialized.
+ QuicTime GetEarliestDeadline() const;
+
+ Perspective perspective_;
+
+ Delegate* delegate_; // Not owned.
+
+ // Initial timeout for how long the wire can have no retransmittable packets.
+ QuicTime::Delta initial_retransmittable_on_wire_timeout_ =
+ QuicTime::Delta::Infinite();
+
+ // Indicates how many consecutive retransmittable-on-wire has been armed
+ // (since last reset).
+ int consecutive_retransmittable_on_wire_count_ = 0;
+
+ // Indicates how many retransmittable-on-wire has been armed in total.
+ int retransmittable_on_wire_count_ = 0;
+
+ QuicTime::Delta keep_alive_timeout_ =
+ QuicTime::Delta::FromSeconds(kPingTimeoutSecs);
+
+ QuicTime retransmittable_on_wire_deadline_ = QuicTime::Zero();
+
+ QuicTime keep_alive_deadline_ = QuicTime::Zero();
+
+ QuicArenaScopedPtr<QuicAlarm> alarm_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_PING_MANAGER_H_
diff --git a/quiche/quic/core/quic_ping_manager_test.cc b/quiche/quic/core/quic_ping_manager_test.cc
new file mode 100644
index 0000000..e7b4b78
--- /dev/null
+++ b/quiche/quic/core/quic_ping_manager_test.cc
@@ -0,0 +1,381 @@
+// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "quiche/quic/core/quic_ping_manager.h"
+
+#include "quiche/quic/core/quic_one_block_arena.h"
+#include "quiche/quic/platform/api/quic_test.h"
+#include "quiche/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+
+class QuicPingManagerPeer {
+ public:
+ static QuicAlarm* GetAlarm(QuicPingManager* manager) {
+ return manager->alarm_.get();
+ }
+};
+
+namespace {
+
+const bool kShouldKeepAlive = true;
+const bool kHasInflightPackets = true;
+
+class MockDelegate : public QuicPingManager::Delegate {
+ public:
+ MOCK_METHOD(void, OnKeepAliveTimeout, (), (override));
+ MOCK_METHOD(void, OnRetransmittableOnWireTimeout, (), (override));
+};
+
+class QuicPingManagerTest : public QuicTest {
+ public:
+ QuicPingManagerTest()
+ : manager_(Perspective::IS_CLIENT, &delegate_, &arena_, &alarm_factory_,
+ /*context=*/nullptr),
+ alarm_(static_cast<MockAlarmFactory::TestAlarm*>(
+ QuicPingManagerPeer::GetAlarm(&manager_))) {
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ }
+
+ protected:
+ testing::StrictMock<MockDelegate> delegate_;
+ MockClock clock_;
+ QuicConnectionArena arena_;
+ MockAlarmFactory alarm_factory_;
+ QuicPingManager manager_;
+ MockAlarmFactory::TestAlarm* alarm_;
+};
+
+TEST_F(QuicPingManagerTest, KeepAliveTimeout) {
+ EXPECT_FALSE(alarm_->IsSet());
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ // Set alarm with in flight packets.
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ kHasInflightPackets);
+ 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);
+ EXPECT_TRUE(alarm_->IsSet());
+ // Verify the deadline is set slightly less than 15 seconds in the future,
+ // because of the 1s alarm granularity.
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs) -
+ QuicTime::Delta::FromMilliseconds(5),
+ alarm_->deadline() - clock_.ApproximateNow());
+
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(kPingTimeoutSecs));
+ EXPECT_CALL(delegate_, OnKeepAliveTimeout());
+ alarm_->Fire();
+ EXPECT_FALSE(alarm_->IsSet());
+ // Reset alarm with in flight packets.
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ kHasInflightPackets);
+ EXPECT_TRUE(alarm_->IsSet());
+
+ // Verify alarm is not armed if !kShouldKeepAlive.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ manager_.SetAlarm(clock_.ApproximateNow(), !kShouldKeepAlive,
+ kHasInflightPackets);
+ EXPECT_FALSE(alarm_->IsSet());
+}
+
+TEST_F(QuicPingManagerTest, CustomizedKeepAliveTimeout) {
+ EXPECT_FALSE(alarm_->IsSet());
+
+ // Set customized keep-alive timeout.
+ manager_.set_keep_alive_timeout(QuicTime::Delta::FromSeconds(10));
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ // Set alarm with in flight packets.
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ kHasInflightPackets);
+ EXPECT_TRUE(alarm_->IsSet());
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(10),
+ alarm_->deadline() - clock_.ApproximateNow());
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ // Set alarm with no in flight packets.
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ !kHasInflightPackets);
+ EXPECT_TRUE(alarm_->IsSet());
+ // The deadline is set slightly less than 10 seconds in the future, because
+ // of the 1s alarm granularity.
+ EXPECT_EQ(
+ QuicTime::Delta::FromSeconds(10) - QuicTime::Delta::FromMilliseconds(5),
+ alarm_->deadline() - clock_.ApproximateNow());
+
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10));
+ EXPECT_CALL(delegate_, OnKeepAliveTimeout());
+ alarm_->Fire();
+ EXPECT_FALSE(alarm_->IsSet());
+ // Reset alarm with in flight packets.
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ kHasInflightPackets);
+ EXPECT_TRUE(alarm_->IsSet());
+
+ // Verify alarm is not armed if !kShouldKeepAlive.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ manager_.SetAlarm(clock_.ApproximateNow(), !kShouldKeepAlive,
+ kHasInflightPackets);
+ EXPECT_FALSE(alarm_->IsSet());
+}
+
+TEST_F(QuicPingManagerTest, RetransmittableOnWireTimeout) {
+ const QuicTime::Delta kRtransmittableOnWireTimeout =
+ QuicTime::Delta::FromMilliseconds(50);
+ manager_.set_initial_retransmittable_on_wire_timeout(
+ kRtransmittableOnWireTimeout);
+
+ EXPECT_FALSE(alarm_->IsSet());
+
+ // Set alarm with in flight packets.
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ kHasInflightPackets);
+ // 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));
+ // Set alarm with no in flight packets.
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ !kHasInflightPackets);
+ EXPECT_TRUE(alarm_->IsSet());
+ // Verify alarm is in retransmittable-on-wire mode.
+ EXPECT_EQ(kRtransmittableOnWireTimeout,
+ alarm_->deadline() - clock_.ApproximateNow());
+
+ clock_.AdvanceTime(kRtransmittableOnWireTimeout);
+ EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout());
+ alarm_->Fire();
+ EXPECT_FALSE(alarm_->IsSet());
+ // Reset alarm with in flight packets.
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ kHasInflightPackets);
+ // Verify the alarm is in keep-alive mode.
+ ASSERT_TRUE(alarm_->IsSet());
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs),
+ alarm_->deadline() - clock_.ApproximateNow());
+}
+
+TEST_F(QuicPingManagerTest, RetransmittableOnWireTimeoutExponentiallyBackOff) {
+ const int kMaxAggressiveRetransmittableOnWireCount = 5;
+ SetQuicFlag(FLAGS_quic_max_aggressive_retransmittable_on_wire_ping_count,
+ kMaxAggressiveRetransmittableOnWireCount);
+ const QuicTime::Delta initial_retransmittable_on_wire_timeout =
+ QuicTime::Delta::FromMilliseconds(200);
+ manager_.set_initial_retransmittable_on_wire_timeout(
+ initial_retransmittable_on_wire_timeout);
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_FALSE(alarm_->IsSet());
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ kHasInflightPackets);
+ // 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);
+ EXPECT_TRUE(alarm_->IsSet());
+ // Verify alarm is in retransmittable-on-wire mode.
+ EXPECT_EQ(initial_retransmittable_on_wire_timeout,
+ alarm_->deadline() - clock_.ApproximateNow());
+ clock_.AdvanceTime(initial_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);
+ }
+
+ QuicTime::Delta retransmittable_on_wire_timeout =
+ initial_retransmittable_on_wire_timeout;
+
+ // 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);
+ 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);
+ }
+
+ // 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);
+ 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());
+}
+
+TEST_F(QuicPingManagerTest,
+ ResetRetransmitableOnWireTimeoutExponentiallyBackOff) {
+ const int kMaxAggressiveRetransmittableOnWireCount = 3;
+ SetQuicFlag(FLAGS_quic_max_aggressive_retransmittable_on_wire_ping_count,
+ kMaxAggressiveRetransmittableOnWireCount);
+ const QuicTime::Delta initial_retransmittable_on_wire_timeout =
+ QuicTime::Delta::FromMilliseconds(200);
+ manager_.set_initial_retransmittable_on_wire_timeout(
+ initial_retransmittable_on_wire_timeout);
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_FALSE(alarm_->IsSet());
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ kHasInflightPackets);
+ // 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));
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ !kHasInflightPackets);
+ EXPECT_TRUE(alarm_->IsSet());
+ // Verify alarm is in retransmittable-on-wire mode.
+ EXPECT_EQ(initial_retransmittable_on_wire_timeout,
+ alarm_->deadline() - clock_.ApproximateNow());
+
+ EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout());
+ clock_.AdvanceTime(initial_retransmittable_on_wire_timeout);
+ alarm_->Fire();
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ !kHasInflightPackets);
+ 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);
+ EXPECT_EQ(initial_retransmittable_on_wire_timeout,
+ alarm_->deadline() - clock_.ApproximateNow());
+
+ for (int i = 0; i < kMaxAggressiveRetransmittableOnWireCount; i++) {
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ !kHasInflightPackets);
+ EXPECT_TRUE(alarm_->IsSet());
+ EXPECT_EQ(initial_retransmittable_on_wire_timeout,
+ alarm_->deadline() - clock_.ApproximateNow());
+ clock_.AdvanceTime(initial_retransmittable_on_wire_timeout);
+ EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout());
+ alarm_->Fire();
+ // Reset alarm with in flight packets.
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ kHasInflightPackets);
+ // Advance 5ms to receive next packet.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ }
+
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ !kHasInflightPackets);
+ EXPECT_TRUE(alarm_->IsSet());
+ EXPECT_EQ(initial_retransmittable_on_wire_timeout * 2,
+ alarm_->deadline() - clock_.ApproximateNow());
+
+ clock_.AdvanceTime(2 * initial_retransmittable_on_wire_timeout);
+ EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout());
+ alarm_->Fire();
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ manager_.reset_consecutive_retransmittable_on_wire_count();
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ !kHasInflightPackets);
+ EXPECT_TRUE(alarm_->IsSet());
+ EXPECT_EQ(initial_retransmittable_on_wire_timeout,
+ alarm_->deadline() - clock_.ApproximateNow());
+}
+
+TEST_F(QuicPingManagerTest, RetransmittableOnWireLimit) {
+ static constexpr int kMaxRetransmittableOnWirePingCount = 3;
+ SetQuicFlag(FLAGS_quic_max_retransmittable_on_wire_ping_count,
+ kMaxRetransmittableOnWirePingCount);
+ static constexpr QuicTime::Delta initial_retransmittable_on_wire_timeout =
+ QuicTime::Delta::FromMilliseconds(200);
+ static constexpr QuicTime::Delta kShortDelay =
+ QuicTime::Delta::FromMilliseconds(5);
+ ASSERT_LT(kShortDelay * 10, initial_retransmittable_on_wire_timeout);
+ manager_.set_initial_retransmittable_on_wire_timeout(
+ initial_retransmittable_on_wire_timeout);
+
+ clock_.AdvanceTime(kShortDelay);
+ EXPECT_FALSE(alarm_->IsSet());
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ kHasInflightPackets);
+
+ EXPECT_TRUE(alarm_->IsSet());
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs),
+ alarm_->deadline() - clock_.ApproximateNow());
+
+ for (int i = 0; i <= kMaxRetransmittableOnWirePingCount; i++) {
+ clock_.AdvanceTime(kShortDelay);
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ !kHasInflightPackets);
+ EXPECT_TRUE(alarm_->IsSet());
+ EXPECT_EQ(initial_retransmittable_on_wire_timeout,
+ alarm_->deadline() - clock_.ApproximateNow());
+ clock_.AdvanceTime(initial_retransmittable_on_wire_timeout);
+ EXPECT_CALL(delegate_, OnRetransmittableOnWireTimeout());
+ alarm_->Fire();
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ kHasInflightPackets);
+ }
+
+ manager_.SetAlarm(clock_.ApproximateNow(), kShouldKeepAlive,
+ !kHasInflightPackets);
+ EXPECT_TRUE(alarm_->IsSet());
+ // Verify alarm is in keep-alive mode.
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs),
+ alarm_->deadline() - clock_.ApproximateNow());
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(kPingTimeoutSecs));
+ EXPECT_CALL(delegate_, OnKeepAliveTimeout());
+ alarm_->Fire();
+ EXPECT_FALSE(alarm_->IsSet());
+}
+
+} // namespace
+
+} // namespace test
+} // namespace quic
diff --git a/quiche/quic/test_tools/quic_connection_peer.cc b/quiche/quic/test_tools/quic_connection_peer.cc
index e124580..9eaa6fb 100644
--- a/quiche/quic/test_tools/quic_connection_peer.cc
+++ b/quiche/quic/test_tools/quic_connection_peer.cc
@@ -64,6 +64,7 @@
Perspective perspective) {
connection->perspective_ = perspective;
QuicFramerPeer::SetPerspective(&connection->framer_, perspective);
+ connection->ping_manager_.perspective_ = perspective;
}
// static
@@ -128,6 +129,9 @@
// static
QuicAlarm* QuicConnectionPeer::GetPingAlarm(QuicConnection* connection) {
+ if (GetQuicReloadableFlag(quic_use_ping_manager)) {
+ return connection->ping_manager_.alarm_.get();
+ }
return connection->ping_alarm_.get();
}