| // Copyright (c) 2013 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/congestion_control/pacing_sender.h" |
| |
| #include "quiche/quic/core/quic_bandwidth.h" |
| #include "quiche/quic/platform/api/quic_flag_utils.h" |
| #include "quiche/quic/platform/api/quic_flags.h" |
| #include "quiche/quic/platform/api/quic_logging.h" |
| |
| namespace quic { |
| namespace { |
| |
| // Configured maximum size of the burst coming out of quiescence. The burst |
| // is never larger than the current CWND in packets. |
| static const uint32_t kInitialUnpacedBurst = 10; |
| |
| } // namespace |
| |
| PacingSender::PacingSender() |
| : sender_(nullptr), |
| max_pacing_rate_(QuicBandwidth::Zero()), |
| burst_tokens_(kInitialUnpacedBurst), |
| ideal_next_packet_send_time_(QuicTime::Zero()), |
| initial_burst_size_(kInitialUnpacedBurst), |
| lumpy_tokens_(0), |
| alarm_granularity_(kAlarmGranularity), |
| pacing_limited_(false) {} |
| |
| PacingSender::~PacingSender() {} |
| |
| void PacingSender::set_sender(SendAlgorithmInterface* sender) { |
| QUICHE_DCHECK(sender != nullptr); |
| sender_ = sender; |
| } |
| |
| void PacingSender::OnCongestionEvent(bool rtt_updated, |
| QuicByteCount bytes_in_flight, |
| QuicTime event_time, |
| const AckedPacketVector& acked_packets, |
| const LostPacketVector& lost_packets) { |
| QUICHE_DCHECK(sender_ != nullptr); |
| if (!lost_packets.empty()) { |
| // Clear any burst tokens when entering recovery. |
| burst_tokens_ = 0; |
| } |
| sender_->OnCongestionEvent(rtt_updated, bytes_in_flight, event_time, |
| acked_packets, lost_packets); |
| } |
| |
| void PacingSender::OnPacketSent( |
| QuicTime sent_time, QuicByteCount bytes_in_flight, |
| QuicPacketNumber packet_number, QuicByteCount bytes, |
| HasRetransmittableData has_retransmittable_data) { |
| QUICHE_DCHECK(sender_ != nullptr); |
| sender_->OnPacketSent(sent_time, bytes_in_flight, packet_number, bytes, |
| has_retransmittable_data); |
| if (has_retransmittable_data != HAS_RETRANSMITTABLE_DATA) { |
| return; |
| } |
| // If in recovery, the connection is not coming out of quiescence. |
| if (bytes_in_flight == 0 && !sender_->InRecovery()) { |
| // Add more burst tokens anytime the connection is leaving quiescence, but |
| // limit it to the equivalent of a single bulk write, not exceeding the |
| // current CWND in packets. |
| burst_tokens_ = std::min( |
| initial_burst_size_, |
| static_cast<uint32_t>(sender_->GetCongestionWindow() / kDefaultTCPMSS)); |
| } |
| if (burst_tokens_ > 0) { |
| --burst_tokens_; |
| ideal_next_packet_send_time_ = QuicTime::Zero(); |
| pacing_limited_ = false; |
| return; |
| } |
| // The next packet should be sent as soon as the current packet has been |
| // transferred. PacingRate is based on bytes in flight including this packet. |
| QuicTime::Delta delay = |
| PacingRate(bytes_in_flight + bytes).TransferTime(bytes); |
| if (!pacing_limited_ || lumpy_tokens_ == 0) { |
| // Reset lumpy_tokens_ if either application or cwnd throttles sending or |
| // token runs out. |
| lumpy_tokens_ = std::max( |
| 1u, std::min(static_cast<uint32_t>(GetQuicFlag(quic_lumpy_pacing_size)), |
| static_cast<uint32_t>( |
| (sender_->GetCongestionWindow() * |
| GetQuicFlag(quic_lumpy_pacing_cwnd_fraction)) / |
| kDefaultTCPMSS))); |
| if (sender_->BandwidthEstimate() < |
| QuicBandwidth::FromKBitsPerSecond( |
| GetQuicFlag(quic_lumpy_pacing_min_bandwidth_kbps))) { |
| // Below 1.2Mbps, send 1 packet at once, because one full-sized packet |
| // is about 10ms of queueing. |
| lumpy_tokens_ = 1u; |
| } |
| if ((bytes_in_flight + bytes) >= sender_->GetCongestionWindow()) { |
| // Don't add lumpy_tokens if the congestion controller is CWND limited. |
| lumpy_tokens_ = 1u; |
| } |
| } |
| --lumpy_tokens_; |
| if (pacing_limited_) { |
| // Make up for lost time since pacing throttles the sending. |
| ideal_next_packet_send_time_ = ideal_next_packet_send_time_ + delay; |
| } else { |
| ideal_next_packet_send_time_ = |
| std::max(ideal_next_packet_send_time_ + delay, sent_time + delay); |
| } |
| // Stop making up for lost time if underlying sender prevents sending. |
| pacing_limited_ = sender_->CanSend(bytes_in_flight + bytes); |
| } |
| |
| void PacingSender::OnApplicationLimited() { |
| // The send is application limited, stop making up for lost time. |
| pacing_limited_ = false; |
| } |
| |
| void PacingSender::SetBurstTokens(uint32_t burst_tokens) { |
| initial_burst_size_ = burst_tokens; |
| burst_tokens_ = std::min( |
| initial_burst_size_, |
| static_cast<uint32_t>(sender_->GetCongestionWindow() / kDefaultTCPMSS)); |
| } |
| |
| QuicTime::Delta PacingSender::TimeUntilSend( |
| QuicTime now, QuicByteCount bytes_in_flight) const { |
| QUICHE_DCHECK(sender_ != nullptr); |
| |
| if (!sender_->CanSend(bytes_in_flight)) { |
| // The underlying sender prevents sending. |
| return QuicTime::Delta::Infinite(); |
| } |
| |
| if (burst_tokens_ > 0 || bytes_in_flight == 0 || lumpy_tokens_ > 0) { |
| // Don't pace if we have burst tokens available or leaving quiescence. |
| QUIC_DVLOG(1) << "Sending packet now. burst_tokens:" << burst_tokens_ |
| << ", bytes_in_flight:" << bytes_in_flight |
| << ", lumpy_tokens:" << lumpy_tokens_; |
| return QuicTime::Delta::Zero(); |
| } |
| |
| // If the next send time is within the alarm granularity, send immediately. |
| if (ideal_next_packet_send_time_ > now + alarm_granularity_) { |
| QUIC_DVLOG(1) << "Delaying packet: " |
| << (ideal_next_packet_send_time_ - now).ToMicroseconds(); |
| return ideal_next_packet_send_time_ - now; |
| } |
| |
| QUIC_DVLOG(1) << "Sending packet now. ideal_next_packet_send_time: " |
| << ideal_next_packet_send_time_ << ", now: " << now; |
| return QuicTime::Delta::Zero(); |
| } |
| |
| QuicBandwidth PacingSender::PacingRate(QuicByteCount bytes_in_flight) const { |
| QUICHE_DCHECK(sender_ != nullptr); |
| if (!max_pacing_rate_.IsZero()) { |
| return QuicBandwidth::FromBitsPerSecond( |
| std::min(max_pacing_rate_.ToBitsPerSecond(), |
| sender_->PacingRate(bytes_in_flight).ToBitsPerSecond())); |
| } |
| return sender_->PacingRate(bytes_in_flight); |
| } |
| |
| } // namespace quic |