Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / net / quic / quic_flow_controller.cc
blob72f4c7fa2f1f09255453dbe513884933acb91904
1 // Copyright 2014 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.
5 #include "net/quic/quic_flow_controller.h"
7 #include "base/basictypes.h"
8 #include "net/quic/quic_connection.h"
9 #include "net/quic/quic_flags.h"
10 #include "net/quic/quic_protocol.h"
12 namespace net {
14 namespace {
15 const QuicByteCount kStreamReceiveWindowLimit = 16 * 1024 * 1024; // 16 MB
16 const QuicByteCount kSessionReceiveWindowLimit = 24 * 1024 * 1024; // 24 MB
19 #define ENDPOINT \
20 (perspective_ == Perspective::IS_SERVER ? "Server: " : "Client: ")
22 QuicFlowController::QuicFlowController(QuicConnection* connection,
23 QuicStreamId id,
24 Perspective perspective,
25 QuicStreamOffset send_window_offset,
26 QuicStreamOffset receive_window_offset,
27 bool should_auto_tune_receive_window)
28 : connection_(connection),
29 id_(id),
30 perspective_(perspective),
31 bytes_sent_(0),
32 send_window_offset_(send_window_offset),
33 bytes_consumed_(0),
34 highest_received_byte_offset_(0),
35 receive_window_offset_(receive_window_offset),
36 receive_window_size_(receive_window_offset),
37 auto_tune_receive_window_(should_auto_tune_receive_window),
38 last_blocked_send_window_offset_(0),
39 prev_window_update_time_(QuicTime::Zero()) {
40 receive_window_size_limit_ = (id_ == kConnectionLevelId)
41 ? kSessionReceiveWindowLimit
42 : kStreamReceiveWindowLimit;
44 DVLOG(1) << ENDPOINT << "Created flow controller for stream " << id_
45 << ", setting initial receive window offset to: "
46 << receive_window_offset_
47 << ", max receive window to: " << receive_window_size_
48 << ", max receive window limit to: " << receive_window_size_limit_
49 << ", setting send window offset to: " << send_window_offset_;
52 void QuicFlowController::AddBytesConsumed(QuicByteCount bytes_consumed) {
53 bytes_consumed_ += bytes_consumed;
54 DVLOG(1) << ENDPOINT << "Stream " << id_ << " consumed: " << bytes_consumed_;
56 MaybeSendWindowUpdate();
59 bool QuicFlowController::UpdateHighestReceivedOffset(
60 QuicStreamOffset new_offset) {
61 // Only update if offset has increased.
62 if (new_offset <= highest_received_byte_offset_) {
63 return false;
66 DVLOG(1) << ENDPOINT << "Stream " << id_
67 << " highest byte offset increased from: "
68 << highest_received_byte_offset_ << " to " << new_offset;
69 highest_received_byte_offset_ = new_offset;
70 return true;
73 void QuicFlowController::AddBytesSent(QuicByteCount bytes_sent) {
74 if (bytes_sent_ + bytes_sent > send_window_offset_) {
75 LOG(DFATAL) << ENDPOINT << "Stream " << id_ << " Trying to send an extra "
76 << bytes_sent << " bytes, when bytes_sent = " << bytes_sent_
77 << ", and send_window_offset_ = " << send_window_offset_;
78 bytes_sent_ = send_window_offset_;
80 // This is an error on our side, close the connection as soon as possible.
81 connection_->SendConnectionClose(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA);
82 return;
85 bytes_sent_ += bytes_sent;
86 DVLOG(1) << ENDPOINT << "Stream " << id_ << " sent: " << bytes_sent_;
89 bool QuicFlowController::FlowControlViolation() {
90 if (highest_received_byte_offset_ > receive_window_offset_) {
91 LOG(ERROR) << ENDPOINT << "Flow control violation on stream "
92 << id_ << ", receive window offset: "
93 << receive_window_offset_
94 << ", highest received byte offset: "
95 << highest_received_byte_offset_;
96 return true;
98 return false;
101 void QuicFlowController::MaybeIncreaseMaxWindowSize() {
102 // Core of receive window auto tuning. This method should be called before a
103 // WINDOW_UPDATE frame is sent. Ideally, window updates should occur close to
104 // once per RTT. If a window update happens much faster than RTT, it implies
105 // that the flow control window is imposing a bottleneck. To prevent this,
106 // this method will increase the receive window size (subject to a reasonable
107 // upper bound). For simplicity this algorithm is deliberately asymmetric, in
108 // that it may increase window size but never decreases.
110 if (!FLAGS_quic_auto_tune_receive_window) {
111 return;
114 // Keep track of timing between successive window updates.
115 QuicTime now = connection_->clock()->ApproximateNow();
116 QuicTime prev = prev_window_update_time_;
117 prev_window_update_time_ = now;
118 if (!prev.IsInitialized()) {
119 DVLOG(1) << ENDPOINT << "first window update for stream " << id_;
120 return;
123 if (!auto_tune_receive_window_) {
124 return;
127 // Get outbound RTT.
128 QuicTime::Delta rtt =
129 connection_->sent_packet_manager().GetRttStats()->smoothed_rtt();
130 if (rtt.IsZero()) {
131 DVLOG(1) << ENDPOINT << "rtt zero for stream " << id_;
132 return;
135 // Now we can compare timing of window updates with RTT.
136 QuicTime::Delta since_last = now.Subtract(prev);
137 QuicTime::Delta two_rtt = rtt.Multiply(2);
139 if (since_last >= two_rtt) {
140 // If interval between window updates is sufficiently large, there
141 // is no need to increase receive_window_size_.
142 return;
145 QuicByteCount old_window = receive_window_size_;
146 receive_window_size_ *= 2;
147 receive_window_size_ =
148 std::min(receive_window_size_, receive_window_size_limit_);
150 if (receive_window_size_ > old_window) {
151 DVLOG(1) << ENDPOINT << "New max window increase for stream " << id_
152 << " after " << since_last.ToMicroseconds() << " us, and RTT is "
153 << rtt.ToMicroseconds()
154 << "us. max wndw: " << receive_window_size_;
155 } else {
156 // TODO(ckrasic) - add a varz to track this (?).
157 DVLOG(1) << ENDPOINT << "Max window at limit for stream " << id_
158 << " after " << since_last.ToMicroseconds() << " us, and RTT is "
159 << rtt.ToMicroseconds()
160 << "us. Limit size: " << receive_window_size_;
164 QuicByteCount QuicFlowController::WindowUpdateThreshold() {
165 return receive_window_size_ / 2;
168 void QuicFlowController::MaybeSendWindowUpdate() {
169 // Send WindowUpdate to increase receive window if
170 // (receive window offset - consumed bytes) < (max window / 2).
171 // This is behaviour copied from SPDY.
172 DCHECK_LE(bytes_consumed_, receive_window_offset_);
173 QuicStreamOffset available_window = receive_window_offset_ - bytes_consumed_;
174 QuicByteCount threshold = WindowUpdateThreshold();
176 if (available_window >= threshold) {
177 DVLOG(1) << ENDPOINT << "Not sending WindowUpdate for stream " << id_
178 << ", available window: " << available_window
179 << ">= threshold: " << threshold;
180 return;
183 MaybeIncreaseMaxWindowSize();
185 // Update our receive window.
186 receive_window_offset_ += (receive_window_size_ - available_window);
188 DVLOG(1) << ENDPOINT << "Sending WindowUpdate frame for stream " << id_
189 << ", consumed bytes: " << bytes_consumed_
190 << ", available window: " << available_window
191 << ", and threshold: " << threshold
192 << ", and receive window size: " << receive_window_size_
193 << ". New receive window offset is: " << receive_window_offset_;
195 // Inform the peer of our new receive window.
196 connection_->SendWindowUpdate(id_, receive_window_offset_);
199 void QuicFlowController::MaybeSendBlocked() {
200 if (SendWindowSize() == 0 &&
201 last_blocked_send_window_offset_ < send_window_offset_) {
202 DVLOG(1) << ENDPOINT << "Stream " << id_ << " is flow control blocked. "
203 << "Send window: " << SendWindowSize()
204 << ", bytes sent: " << bytes_sent_
205 << ", send limit: " << send_window_offset_;
206 // The entire send_window has been consumed, we are now flow control
207 // blocked.
208 connection_->SendBlocked(id_);
210 // Keep track of when we last sent a BLOCKED frame so that we only send one
211 // at a given send offset.
212 last_blocked_send_window_offset_ = send_window_offset_;
216 bool QuicFlowController::UpdateSendWindowOffset(
217 QuicStreamOffset new_send_window_offset) {
218 // Only update if send window has increased.
219 if (new_send_window_offset <= send_window_offset_) {
220 return false;
223 DVLOG(1) << ENDPOINT << "UpdateSendWindowOffset for stream " << id_
224 << " with new offset " << new_send_window_offset
225 << " current offset: " << send_window_offset_
226 << " bytes_sent: " << bytes_sent_;
228 const bool blocked = IsBlocked();
229 send_window_offset_ = new_send_window_offset;
230 return blocked;
233 bool QuicFlowController::IsBlocked() const {
234 return SendWindowSize() == 0;
237 uint64 QuicFlowController::SendWindowSize() const {
238 if (bytes_sent_ > send_window_offset_) {
239 return 0;
241 return send_window_offset_ - bytes_sent_;
244 void QuicFlowController::UpdateReceiveWindowSize(QuicStreamOffset size) {
245 DVLOG(1) << ENDPOINT << "UpdateReceiveWindowSize for stream " << id_ << ": "
246 << size;
247 if (receive_window_size_ != receive_window_offset_) {
248 LOG(DFATAL) << "receive_window_size_:" << receive_window_size_
249 << " != receive_window_offset:" << receive_window_offset_;
250 return;
252 receive_window_size_ = size;
253 receive_window_offset_ = size;
256 } // namespace net