1 // Copyright (c) 2020 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_idle_network_detector.h"
6
7 #include "quiche/quic/core/quic_constants.h"
8 #include "quiche/quic/core/quic_time.h"
9 #include "quiche/quic/platform/api/quic_flag_utils.h"
10 #include "quiche/quic/platform/api/quic_flags.h"
11 #include "quiche/common/platform/api/quiche_logging.h"
12
13 namespace quic {
14
15 namespace {
16
17 class AlarmDelegate : public QuicAlarm::DelegateWithContext {
18 public:
AlarmDelegate(QuicIdleNetworkDetector * detector,QuicConnectionContext * context)19 explicit AlarmDelegate(QuicIdleNetworkDetector* detector,
20 QuicConnectionContext* context)
21 : QuicAlarm::DelegateWithContext(context), detector_(detector) {}
22 AlarmDelegate(const AlarmDelegate&) = delete;
23 AlarmDelegate& operator=(const AlarmDelegate&) = delete;
24
OnAlarm()25 void OnAlarm() override { detector_->OnAlarm(); }
26
27 private:
28 QuicIdleNetworkDetector* detector_;
29 };
30
31 } // namespace
32
QuicIdleNetworkDetector(Delegate * delegate,QuicTime now,QuicConnectionArena * arena,QuicAlarmFactory * alarm_factory,QuicConnectionContext * context)33 QuicIdleNetworkDetector::QuicIdleNetworkDetector(
34 Delegate* delegate, QuicTime now, QuicConnectionArena* arena,
35 QuicAlarmFactory* alarm_factory, QuicConnectionContext* context)
36 : delegate_(delegate),
37 start_time_(now),
38 handshake_timeout_(QuicTime::Delta::Infinite()),
39 time_of_last_received_packet_(now),
40 time_of_first_packet_sent_after_receiving_(QuicTime::Zero()),
41 idle_network_timeout_(QuicTime::Delta::Infinite()),
42 bandwidth_update_timeout_(QuicTime::Delta::Infinite()),
43 alarm_(alarm_factory->CreateAlarm(
44 arena->New<AlarmDelegate>(this, context), arena)) {}
45
OnAlarm()46 void QuicIdleNetworkDetector::OnAlarm() {
47 if (!bandwidth_update_timeout_.IsInfinite()) {
48 QUICHE_DCHECK(handshake_timeout_.IsInfinite());
49 bandwidth_update_timeout_ = QuicTime::Delta::Infinite();
50 SetAlarm();
51 delegate_->OnBandwidthUpdateTimeout();
52 return;
53 }
54 if (handshake_timeout_.IsInfinite()) {
55 delegate_->OnIdleNetworkDetected();
56 return;
57 }
58 if (idle_network_timeout_.IsInfinite()) {
59 delegate_->OnHandshakeTimeout();
60 return;
61 }
62 if (last_network_activity_time() + idle_network_timeout_ >
63 start_time_ + handshake_timeout_) {
64 delegate_->OnHandshakeTimeout();
65 return;
66 }
67 delegate_->OnIdleNetworkDetected();
68 }
69
SetTimeouts(QuicTime::Delta handshake_timeout,QuicTime::Delta idle_network_timeout)70 void QuicIdleNetworkDetector::SetTimeouts(
71 QuicTime::Delta handshake_timeout, QuicTime::Delta idle_network_timeout) {
72 handshake_timeout_ = handshake_timeout;
73 idle_network_timeout_ = idle_network_timeout;
74 bandwidth_update_timeout_ = QuicTime::Delta::Infinite();
75
76 if (GetQuicRestartFlag(
77 quic_enable_sending_bandwidth_estimate_when_network_idle_v2) &&
78 handshake_timeout_.IsInfinite()) {
79 QUIC_RESTART_FLAG_COUNT_N(
80 quic_enable_sending_bandwidth_estimate_when_network_idle_v2, 1, 3);
81 bandwidth_update_timeout_ = idle_network_timeout_ * 0.5;
82 }
83
84 SetAlarm();
85 }
86
StopDetection()87 void QuicIdleNetworkDetector::StopDetection() {
88 alarm_->PermanentCancel();
89 handshake_timeout_ = QuicTime::Delta::Infinite();
90 idle_network_timeout_ = QuicTime::Delta::Infinite();
91 handshake_timeout_ = QuicTime::Delta::Infinite();
92 stopped_ = true;
93 }
94
OnPacketSent(QuicTime now,QuicTime::Delta pto_delay)95 void QuicIdleNetworkDetector::OnPacketSent(QuicTime now,
96 QuicTime::Delta pto_delay) {
97 if (time_of_first_packet_sent_after_receiving_ >
98 time_of_last_received_packet_) {
99 return;
100 }
101 time_of_first_packet_sent_after_receiving_ =
102 std::max(time_of_first_packet_sent_after_receiving_, now);
103 if (shorter_idle_timeout_on_sent_packet_) {
104 MaybeSetAlarmOnSentPacket(pto_delay);
105 return;
106 }
107
108 SetAlarm();
109 }
110
OnPacketReceived(QuicTime now)111 void QuicIdleNetworkDetector::OnPacketReceived(QuicTime now) {
112 time_of_last_received_packet_ = std::max(time_of_last_received_packet_, now);
113
114 SetAlarm();
115 }
116
SetAlarm()117 void QuicIdleNetworkDetector::SetAlarm() {
118 if (stopped_) {
119 // TODO(wub): If this QUIC_BUG fires, it indicates a problem in the
120 // QuicConnection, which somehow called this function while disconnected.
121 // That problem needs to be fixed.
122 QUIC_BUG(quic_idle_detector_set_alarm_after_stopped)
123 << "SetAlarm called after stopped";
124 return;
125 }
126 // Set alarm to the nearer deadline.
127 QuicTime new_deadline = QuicTime::Zero();
128 if (!handshake_timeout_.IsInfinite()) {
129 new_deadline = start_time_ + handshake_timeout_;
130 }
131 if (!idle_network_timeout_.IsInfinite()) {
132 const QuicTime idle_network_deadline = GetIdleNetworkDeadline();
133 if (new_deadline.IsInitialized()) {
134 new_deadline = std::min(new_deadline, idle_network_deadline);
135 } else {
136 new_deadline = idle_network_deadline;
137 }
138 }
139 if (!bandwidth_update_timeout_.IsInfinite()) {
140 new_deadline = std::min(new_deadline, GetBandwidthUpdateDeadline());
141 }
142 alarm_->Update(new_deadline, kAlarmGranularity);
143 }
144
MaybeSetAlarmOnSentPacket(QuicTime::Delta pto_delay)145 void QuicIdleNetworkDetector::MaybeSetAlarmOnSentPacket(
146 QuicTime::Delta pto_delay) {
147 QUICHE_DCHECK(shorter_idle_timeout_on_sent_packet_);
148 if (!handshake_timeout_.IsInfinite() || !alarm_->IsSet()) {
149 SetAlarm();
150 return;
151 }
152 // Make sure connection will be alive for another PTO.
153 const QuicTime deadline = alarm_->deadline();
154 const QuicTime min_deadline = last_network_activity_time() + pto_delay;
155 if (deadline > min_deadline) {
156 return;
157 }
158 alarm_->Update(min_deadline, kAlarmGranularity);
159 }
160
GetIdleNetworkDeadline() const161 QuicTime QuicIdleNetworkDetector::GetIdleNetworkDeadline() const {
162 if (idle_network_timeout_.IsInfinite()) {
163 return QuicTime::Zero();
164 }
165 return last_network_activity_time() + idle_network_timeout_;
166 }
167
GetBandwidthUpdateDeadline() const168 QuicTime QuicIdleNetworkDetector::GetBandwidthUpdateDeadline() const {
169 QUICHE_DCHECK(!bandwidth_update_timeout_.IsInfinite());
170 return last_network_activity_time() + bandwidth_update_timeout_;
171 }
172
173 } // namespace quic
174