xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/core/quic_ping_manager.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright (c) 2022 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "quiche/quic/core/quic_ping_manager.h"
6 
7 #include "quiche/quic/platform/api/quic_flags.h"
8 
9 namespace quic {
10 
11 namespace {
12 
13 // Maximum shift used to calculate retransmittable on wire timeout. For 200ms
14 // initial retransmittable on wire delay, this would get a maximum of 200ms * (1
15 // << 10) = 204.8s
16 const int kMaxRetransmittableOnWireDelayShift = 10;
17 
18 class AlarmDelegate : public QuicAlarm::DelegateWithContext {
19  public:
AlarmDelegate(QuicPingManager * manager,QuicConnectionContext * context)20   explicit AlarmDelegate(QuicPingManager* manager,
21                          QuicConnectionContext* context)
22       : QuicAlarm::DelegateWithContext(context), manager_(manager) {}
23   AlarmDelegate(const AlarmDelegate&) = delete;
24   AlarmDelegate& operator=(const AlarmDelegate&) = delete;
25 
OnAlarm()26   void OnAlarm() override { manager_->OnAlarm(); }
27 
28  private:
29   QuicPingManager* manager_;
30 };
31 
32 }  // namespace
33 
QuicPingManager(Perspective perspective,Delegate * delegate,QuicConnectionArena * arena,QuicAlarmFactory * alarm_factory,QuicConnectionContext * context)34 QuicPingManager::QuicPingManager(Perspective perspective, Delegate* delegate,
35                                  QuicConnectionArena* arena,
36                                  QuicAlarmFactory* alarm_factory,
37                                  QuicConnectionContext* context)
38     : perspective_(perspective),
39       delegate_(delegate),
40       alarm_(alarm_factory->CreateAlarm(
41           arena->New<AlarmDelegate>(this, context), arena)) {}
42 
SetAlarm(QuicTime now,bool should_keep_alive,bool has_in_flight_packets)43 void QuicPingManager::SetAlarm(QuicTime now, bool should_keep_alive,
44                                bool has_in_flight_packets) {
45   UpdateDeadlines(now, should_keep_alive, has_in_flight_packets);
46   const QuicTime earliest_deadline = GetEarliestDeadline();
47   if (!earliest_deadline.IsInitialized()) {
48     alarm_->Cancel();
49     return;
50   }
51   if (earliest_deadline == keep_alive_deadline_) {
52     // Use 1s granularity for keep-alive time.
53     alarm_->Update(earliest_deadline, QuicTime::Delta::FromSeconds(1));
54     return;
55   }
56   alarm_->Update(earliest_deadline, kAlarmGranularity);
57 }
58 
OnAlarm()59 void QuicPingManager::OnAlarm() {
60   const QuicTime earliest_deadline = GetEarliestDeadline();
61   if (!earliest_deadline.IsInitialized()) {
62     QUIC_BUG(quic_ping_manager_alarm_fires_unexpectedly)
63         << "QuicPingManager alarm fires unexpectedly.";
64     return;
65   }
66   // Please note, alarm does not get re-armed here, and we are relying on caller
67   // to SetAlarm later.
68   if (earliest_deadline == retransmittable_on_wire_deadline_) {
69     retransmittable_on_wire_deadline_ = QuicTime::Zero();
70     if (GetQuicFlag(quic_max_aggressive_retransmittable_on_wire_ping_count) !=
71         0) {
72       ++consecutive_retransmittable_on_wire_count_;
73     }
74     ++retransmittable_on_wire_count_;
75     delegate_->OnRetransmittableOnWireTimeout();
76     return;
77   }
78   if (earliest_deadline == keep_alive_deadline_) {
79     keep_alive_deadline_ = QuicTime::Zero();
80     delegate_->OnKeepAliveTimeout();
81   }
82 }
83 
Stop()84 void QuicPingManager::Stop() {
85   alarm_->PermanentCancel();
86   retransmittable_on_wire_deadline_ = QuicTime::Zero();
87   keep_alive_deadline_ = QuicTime::Zero();
88 }
89 
UpdateDeadlines(QuicTime now,bool should_keep_alive,bool has_in_flight_packets)90 void QuicPingManager::UpdateDeadlines(QuicTime now, bool should_keep_alive,
91                                       bool has_in_flight_packets) {
92   // Reset keep-alive deadline given it will be set later (with left edge
93   // |now|).
94   keep_alive_deadline_ = QuicTime::Zero();
95   if (perspective_ == Perspective::IS_SERVER &&
96       initial_retransmittable_on_wire_timeout_.IsInfinite()) {
97     // The PING alarm exists to support two features:
98     // 1) clients send PINGs every 15s to prevent NAT timeouts,
99     // 2) both clients and servers can send retransmittable on the wire PINGs
100     // (ROWP) while ShouldKeepConnectionAlive is true and there is no packets in
101     // flight.
102     QUICHE_DCHECK(!retransmittable_on_wire_deadline_.IsInitialized());
103     return;
104   }
105   if (!should_keep_alive) {
106     // Don't send a ping unless the application (ie: HTTP/3) says to, usually
107     // because it is expecting a response from the peer.
108     retransmittable_on_wire_deadline_ = QuicTime::Zero();
109     return;
110   }
111   if (perspective_ == Perspective::IS_CLIENT) {
112     // Clients send 15s PINGs to avoid NATs from timing out.
113     keep_alive_deadline_ = now + keep_alive_timeout_;
114   }
115   if (initial_retransmittable_on_wire_timeout_.IsInfinite() ||
116       has_in_flight_packets ||
117       retransmittable_on_wire_count_ >
118           GetQuicFlag(quic_max_retransmittable_on_wire_ping_count)) {
119     // No need to set retransmittable-on-wire timeout.
120     retransmittable_on_wire_deadline_ = QuicTime::Zero();
121     return;
122   }
123 
124   QUICHE_DCHECK_LT(initial_retransmittable_on_wire_timeout_,
125                    keep_alive_timeout_);
126   QuicTime::Delta retransmittable_on_wire_timeout =
127       initial_retransmittable_on_wire_timeout_;
128   const int max_aggressive_retransmittable_on_wire_count =
129       GetQuicFlag(quic_max_aggressive_retransmittable_on_wire_ping_count);
130   QUICHE_DCHECK_LE(0, max_aggressive_retransmittable_on_wire_count);
131   if (consecutive_retransmittable_on_wire_count_ >
132       max_aggressive_retransmittable_on_wire_count) {
133     // Exponentially back off the timeout if the number of consecutive
134     // retransmittable on wire pings has exceeds the allowance.
135     int shift = std::min(consecutive_retransmittable_on_wire_count_ -
136                              max_aggressive_retransmittable_on_wire_count,
137                          kMaxRetransmittableOnWireDelayShift);
138     retransmittable_on_wire_timeout =
139         initial_retransmittable_on_wire_timeout_ * (1 << shift);
140   }
141   if (retransmittable_on_wire_deadline_.IsInitialized() &&
142       retransmittable_on_wire_deadline_ <
143           now + retransmittable_on_wire_timeout) {
144     // Alarm is set to an earlier time. Do not postpone it.
145     return;
146   }
147   retransmittable_on_wire_deadline_ = now + retransmittable_on_wire_timeout;
148 }
149 
GetEarliestDeadline() const150 QuicTime QuicPingManager::GetEarliestDeadline() const {
151   QuicTime earliest_deadline = QuicTime::Zero();
152   for (QuicTime t : {retransmittable_on_wire_deadline_, keep_alive_deadline_}) {
153     if (!t.IsInitialized()) {
154       continue;
155     }
156     if (!earliest_deadline.IsInitialized() || t < earliest_deadline) {
157       earliest_deadline = t;
158     }
159   }
160   return earliest_deadline;
161 }
162 
163 }  // namespace quic
164