summaryrefslogtreecommitdiff
path: root/chromium/net/third_party/quiche/src/quiche/quic/core/congestion_control/bandwidth_sampler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/third_party/quiche/src/quiche/quic/core/congestion_control/bandwidth_sampler.cc')
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/congestion_control/bandwidth_sampler.cc583
1 files changed, 583 insertions, 0 deletions
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/congestion_control/bandwidth_sampler.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/congestion_control/bandwidth_sampler.cc
new file mode 100644
index 00000000000..e216d09ba60
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/congestion_control/bandwidth_sampler.cc
@@ -0,0 +1,583 @@
+// Copyright 2016 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/bandwidth_sampler.h"
+
+#include <algorithm>
+
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/platform/api/quic_bug_tracker.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 {
+
+std::ostream& operator<<(std::ostream& os, const SendTimeState& s) {
+ os << "{valid:" << s.is_valid << ", app_limited:" << s.is_app_limited
+ << ", total_sent:" << s.total_bytes_sent
+ << ", total_acked:" << s.total_bytes_acked
+ << ", total_lost:" << s.total_bytes_lost
+ << ", inflight:" << s.bytes_in_flight << "}";
+ return os;
+}
+
+QuicByteCount MaxAckHeightTracker::Update(
+ QuicBandwidth bandwidth_estimate, bool is_new_max_bandwidth,
+ QuicRoundTripCount round_trip_count,
+ QuicPacketNumber last_sent_packet_number,
+ QuicPacketNumber last_acked_packet_number, QuicTime ack_time,
+ QuicByteCount bytes_acked) {
+ bool force_new_epoch = false;
+
+ if (reduce_extra_acked_on_bandwidth_increase_ && is_new_max_bandwidth) {
+ // Save and clear existing entries.
+ ExtraAckedEvent best = max_ack_height_filter_.GetBest();
+ ExtraAckedEvent second_best = max_ack_height_filter_.GetSecondBest();
+ ExtraAckedEvent third_best = max_ack_height_filter_.GetThirdBest();
+ max_ack_height_filter_.Clear();
+
+ // Reinsert the heights into the filter after recalculating.
+ QuicByteCount expected_bytes_acked = bandwidth_estimate * best.time_delta;
+ if (expected_bytes_acked < best.bytes_acked) {
+ best.extra_acked = best.bytes_acked - expected_bytes_acked;
+ max_ack_height_filter_.Update(best, best.round);
+ }
+ expected_bytes_acked = bandwidth_estimate * second_best.time_delta;
+ if (expected_bytes_acked < second_best.bytes_acked) {
+ QUICHE_DCHECK_LE(best.round, second_best.round);
+ second_best.extra_acked = second_best.bytes_acked - expected_bytes_acked;
+ max_ack_height_filter_.Update(second_best, second_best.round);
+ }
+ expected_bytes_acked = bandwidth_estimate * third_best.time_delta;
+ if (expected_bytes_acked < third_best.bytes_acked) {
+ QUICHE_DCHECK_LE(second_best.round, third_best.round);
+ third_best.extra_acked = third_best.bytes_acked - expected_bytes_acked;
+ max_ack_height_filter_.Update(third_best, third_best.round);
+ }
+ }
+
+ // If any packet sent after the start of the epoch has been acked, start a new
+ // epoch.
+ if (start_new_aggregation_epoch_after_full_round_ &&
+ last_sent_packet_number_before_epoch_.IsInitialized() &&
+ last_acked_packet_number.IsInitialized() &&
+ last_acked_packet_number > last_sent_packet_number_before_epoch_) {
+ QUIC_DVLOG(3) << "Force starting a new aggregation epoch. "
+ "last_sent_packet_number_before_epoch_:"
+ << last_sent_packet_number_before_epoch_
+ << ", last_acked_packet_number:" << last_acked_packet_number;
+ if (reduce_extra_acked_on_bandwidth_increase_) {
+ QUIC_BUG(quic_bwsampler_46)
+ << "A full round of aggregation should never "
+ << "pass with startup_include_extra_acked(B204) enabled.";
+ }
+ force_new_epoch = true;
+ }
+ if (aggregation_epoch_start_time_ == QuicTime::Zero() || force_new_epoch) {
+ aggregation_epoch_bytes_ = bytes_acked;
+ aggregation_epoch_start_time_ = ack_time;
+ last_sent_packet_number_before_epoch_ = last_sent_packet_number;
+ ++num_ack_aggregation_epochs_;
+ return 0;
+ }
+
+ // Compute how many bytes are expected to be delivered, assuming max bandwidth
+ // is correct.
+ QuicTime::Delta aggregation_delta = ack_time - aggregation_epoch_start_time_;
+ QuicByteCount expected_bytes_acked = bandwidth_estimate * aggregation_delta;
+ // Reset the current aggregation epoch as soon as the ack arrival rate is less
+ // than or equal to the max bandwidth.
+ if (aggregation_epoch_bytes_ <=
+ ack_aggregation_bandwidth_threshold_ * expected_bytes_acked) {
+ QUIC_DVLOG(3) << "Starting a new aggregation epoch because "
+ "aggregation_epoch_bytes_ "
+ << aggregation_epoch_bytes_
+ << " is smaller than expected. "
+ "ack_aggregation_bandwidth_threshold_:"
+ << ack_aggregation_bandwidth_threshold_
+ << ", expected_bytes_acked:" << expected_bytes_acked
+ << ", bandwidth_estimate:" << bandwidth_estimate
+ << ", aggregation_duration:" << aggregation_delta
+ << ", new_aggregation_epoch:" << ack_time
+ << ", new_aggregation_bytes_acked:" << bytes_acked;
+ // Reset to start measuring a new aggregation epoch.
+ aggregation_epoch_bytes_ = bytes_acked;
+ aggregation_epoch_start_time_ = ack_time;
+ last_sent_packet_number_before_epoch_ = last_sent_packet_number;
+ ++num_ack_aggregation_epochs_;
+ return 0;
+ }
+
+ aggregation_epoch_bytes_ += bytes_acked;
+
+ // Compute how many extra bytes were delivered vs max bandwidth.
+ QuicByteCount extra_bytes_acked =
+ aggregation_epoch_bytes_ - expected_bytes_acked;
+ QUIC_DVLOG(3) << "Updating MaxAckHeight. ack_time:" << ack_time
+ << ", last sent packet:" << last_sent_packet_number
+ << ", bandwidth_estimate:" << bandwidth_estimate
+ << ", bytes_acked:" << bytes_acked
+ << ", expected_bytes_acked:" << expected_bytes_acked
+ << ", aggregation_epoch_bytes_:" << aggregation_epoch_bytes_
+ << ", extra_bytes_acked:" << extra_bytes_acked;
+ ExtraAckedEvent new_event;
+ new_event.extra_acked = extra_bytes_acked;
+ new_event.bytes_acked = aggregation_epoch_bytes_;
+ new_event.time_delta = aggregation_delta;
+ max_ack_height_filter_.Update(new_event, round_trip_count);
+ return extra_bytes_acked;
+}
+
+BandwidthSampler::BandwidthSampler(
+ const QuicUnackedPacketMap* unacked_packet_map,
+ QuicRoundTripCount max_height_tracker_window_length)
+ : total_bytes_sent_(0),
+ total_bytes_acked_(0),
+ total_bytes_lost_(0),
+ total_bytes_neutered_(0),
+ total_bytes_sent_at_last_acked_packet_(0),
+ last_acked_packet_sent_time_(QuicTime::Zero()),
+ last_acked_packet_ack_time_(QuicTime::Zero()),
+ is_app_limited_(true),
+ connection_state_map_(),
+ max_tracked_packets_(GetQuicFlag(FLAGS_quic_max_tracked_packet_count)),
+ unacked_packet_map_(unacked_packet_map),
+ max_ack_height_tracker_(max_height_tracker_window_length),
+ total_bytes_acked_after_last_ack_event_(0),
+ overestimate_avoidance_(false),
+ limit_max_ack_height_tracker_by_send_rate_(false) {}
+
+BandwidthSampler::BandwidthSampler(const BandwidthSampler& other)
+ : total_bytes_sent_(other.total_bytes_sent_),
+ total_bytes_acked_(other.total_bytes_acked_),
+ total_bytes_lost_(other.total_bytes_lost_),
+ total_bytes_neutered_(other.total_bytes_neutered_),
+ total_bytes_sent_at_last_acked_packet_(
+ other.total_bytes_sent_at_last_acked_packet_),
+ last_acked_packet_sent_time_(other.last_acked_packet_sent_time_),
+ last_acked_packet_ack_time_(other.last_acked_packet_ack_time_),
+ last_sent_packet_(other.last_sent_packet_),
+ last_acked_packet_(other.last_acked_packet_),
+ is_app_limited_(other.is_app_limited_),
+ end_of_app_limited_phase_(other.end_of_app_limited_phase_),
+ connection_state_map_(other.connection_state_map_),
+ recent_ack_points_(other.recent_ack_points_),
+ a0_candidates_(other.a0_candidates_),
+ max_tracked_packets_(other.max_tracked_packets_),
+ unacked_packet_map_(other.unacked_packet_map_),
+ max_ack_height_tracker_(other.max_ack_height_tracker_),
+ total_bytes_acked_after_last_ack_event_(
+ other.total_bytes_acked_after_last_ack_event_),
+ overestimate_avoidance_(other.overestimate_avoidance_),
+ limit_max_ack_height_tracker_by_send_rate_(
+ other.limit_max_ack_height_tracker_by_send_rate_) {}
+
+void BandwidthSampler::EnableOverestimateAvoidance() {
+ if (overestimate_avoidance_) {
+ return;
+ }
+
+ overestimate_avoidance_ = true;
+ // TODO(wub): Change the default value of
+ // --quic_ack_aggregation_bandwidth_threshold to 2.0.
+ max_ack_height_tracker_.SetAckAggregationBandwidthThreshold(2.0);
+}
+
+BandwidthSampler::~BandwidthSampler() {}
+
+void BandwidthSampler::OnPacketSent(
+ QuicTime sent_time, QuicPacketNumber packet_number, QuicByteCount bytes,
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) {
+ last_sent_packet_ = packet_number;
+
+ if (has_retransmittable_data != HAS_RETRANSMITTABLE_DATA) {
+ return;
+ }
+
+ total_bytes_sent_ += bytes;
+
+ // If there are no packets in flight, the time at which the new transmission
+ // opens can be treated as the A_0 point for the purpose of bandwidth
+ // sampling. This underestimates bandwidth to some extent, and produces some
+ // artificially low samples for most packets in flight, but it provides with
+ // samples at important points where we would not have them otherwise, most
+ // importantly at the beginning of the connection.
+ if (bytes_in_flight == 0) {
+ last_acked_packet_ack_time_ = sent_time;
+ if (overestimate_avoidance_) {
+ recent_ack_points_.Clear();
+ recent_ack_points_.Update(sent_time, total_bytes_acked_);
+ a0_candidates_.clear();
+ a0_candidates_.push_back(recent_ack_points_.MostRecentPoint());
+ }
+ total_bytes_sent_at_last_acked_packet_ = total_bytes_sent_;
+
+ // In this situation ack compression is not a concern, set send rate to
+ // effectively infinite.
+ last_acked_packet_sent_time_ = sent_time;
+ }
+
+ if (!connection_state_map_.IsEmpty() &&
+ packet_number >
+ connection_state_map_.last_packet() + max_tracked_packets_) {
+ if (unacked_packet_map_ != nullptr && !unacked_packet_map_->empty()) {
+ QuicPacketNumber maybe_least_unacked =
+ unacked_packet_map_->GetLeastUnacked();
+ QUIC_BUG(quic_bug_10437_1)
+ << "BandwidthSampler in-flight packet map has exceeded maximum "
+ "number of tracked packets("
+ << max_tracked_packets_
+ << "). First tracked: " << connection_state_map_.first_packet()
+ << "; last tracked: " << connection_state_map_.last_packet()
+ << "; entry_slots_used: " << connection_state_map_.entry_slots_used()
+ << "; number_of_present_entries: "
+ << connection_state_map_.number_of_present_entries()
+ << "; packet number: " << packet_number
+ << "; unacked_map: " << unacked_packet_map_->DebugString()
+ << "; total_bytes_sent: " << total_bytes_sent_
+ << "; total_bytes_acked: " << total_bytes_acked_
+ << "; total_bytes_lost: " << total_bytes_lost_
+ << "; total_bytes_neutered: " << total_bytes_neutered_
+ << "; last_acked_packet_sent_time: " << last_acked_packet_sent_time_
+ << "; total_bytes_sent_at_last_acked_packet: "
+ << total_bytes_sent_at_last_acked_packet_
+ << "; least_unacked_packet_info: "
+ << (unacked_packet_map_->IsUnacked(maybe_least_unacked)
+ ? unacked_packet_map_
+ ->GetTransmissionInfo(maybe_least_unacked)
+ .DebugString()
+ : "n/a");
+ } else {
+ QUIC_BUG(quic_bug_10437_2)
+ << "BandwidthSampler in-flight packet map has exceeded maximum "
+ "number of tracked packets.";
+ }
+ }
+
+ bool success = connection_state_map_.Emplace(packet_number, sent_time, bytes,
+ bytes_in_flight + bytes, *this);
+ QUIC_BUG_IF(quic_bug_10437_3, !success)
+ << "BandwidthSampler failed to insert the packet "
+ "into the map, most likely because it's already "
+ "in it.";
+}
+
+void BandwidthSampler::OnPacketNeutered(QuicPacketNumber packet_number) {
+ connection_state_map_.Remove(
+ packet_number, [&](const ConnectionStateOnSentPacket& sent_packet) {
+ QUIC_CODE_COUNT(quic_bandwidth_sampler_packet_neutered);
+ total_bytes_neutered_ += sent_packet.size;
+ });
+}
+
+BandwidthSamplerInterface::CongestionEventSample
+BandwidthSampler::OnCongestionEvent(QuicTime ack_time,
+ const AckedPacketVector& acked_packets,
+ const LostPacketVector& lost_packets,
+ QuicBandwidth max_bandwidth,
+ QuicBandwidth est_bandwidth_upper_bound,
+ QuicRoundTripCount round_trip_count) {
+ CongestionEventSample event_sample;
+
+ SendTimeState last_lost_packet_send_state;
+
+ for (const LostPacket& packet : lost_packets) {
+ SendTimeState send_state =
+ OnPacketLost(packet.packet_number, packet.bytes_lost);
+ if (send_state.is_valid) {
+ last_lost_packet_send_state = send_state;
+ }
+ }
+
+ if (acked_packets.empty()) {
+ // Only populate send state for a loss-only event.
+ event_sample.last_packet_send_state = last_lost_packet_send_state;
+ return event_sample;
+ }
+
+ SendTimeState last_acked_packet_send_state;
+ QuicBandwidth max_send_rate = QuicBandwidth::Zero();
+ for (const auto& packet : acked_packets) {
+ BandwidthSample sample =
+ OnPacketAcknowledged(ack_time, packet.packet_number);
+ if (!sample.state_at_send.is_valid) {
+ continue;
+ }
+
+ last_acked_packet_send_state = sample.state_at_send;
+
+ if (!sample.rtt.IsZero()) {
+ event_sample.sample_rtt = std::min(event_sample.sample_rtt, sample.rtt);
+ }
+ if (sample.bandwidth > event_sample.sample_max_bandwidth) {
+ event_sample.sample_max_bandwidth = sample.bandwidth;
+ event_sample.sample_is_app_limited = sample.state_at_send.is_app_limited;
+ }
+ if (!sample.send_rate.IsInfinite()) {
+ max_send_rate = std::max(max_send_rate, sample.send_rate);
+ }
+ const QuicByteCount inflight_sample =
+ total_bytes_acked() - last_acked_packet_send_state.total_bytes_acked;
+ if (inflight_sample > event_sample.sample_max_inflight) {
+ event_sample.sample_max_inflight = inflight_sample;
+ }
+ }
+
+ if (!last_lost_packet_send_state.is_valid) {
+ event_sample.last_packet_send_state = last_acked_packet_send_state;
+ } else if (!last_acked_packet_send_state.is_valid) {
+ event_sample.last_packet_send_state = last_lost_packet_send_state;
+ } else {
+ // If two packets are inflight and an alarm is armed to lose a packet and it
+ // wakes up late, then the first of two in flight packets could have been
+ // acknowledged before the wakeup, which re-evaluates loss detection, and
+ // could declare the later of the two lost.
+ event_sample.last_packet_send_state =
+ lost_packets.back().packet_number > acked_packets.back().packet_number
+ ? last_lost_packet_send_state
+ : last_acked_packet_send_state;
+ }
+
+ bool is_new_max_bandwidth = event_sample.sample_max_bandwidth > max_bandwidth;
+ max_bandwidth = std::max(max_bandwidth, event_sample.sample_max_bandwidth);
+ if (limit_max_ack_height_tracker_by_send_rate_) {
+ max_bandwidth = std::max(max_bandwidth, max_send_rate);
+ }
+ // TODO(ianswett): Why is the min being passed in here?
+ event_sample.extra_acked =
+ OnAckEventEnd(std::min(est_bandwidth_upper_bound, max_bandwidth),
+ is_new_max_bandwidth, round_trip_count);
+
+ return event_sample;
+}
+
+QuicByteCount BandwidthSampler::OnAckEventEnd(
+ QuicBandwidth bandwidth_estimate, bool is_new_max_bandwidth,
+ QuicRoundTripCount round_trip_count) {
+ const QuicByteCount newly_acked_bytes =
+ total_bytes_acked_ - total_bytes_acked_after_last_ack_event_;
+
+ if (newly_acked_bytes == 0) {
+ return 0;
+ }
+ total_bytes_acked_after_last_ack_event_ = total_bytes_acked_;
+ QuicByteCount extra_acked = max_ack_height_tracker_.Update(
+ bandwidth_estimate, is_new_max_bandwidth, round_trip_count,
+ last_sent_packet_, last_acked_packet_, last_acked_packet_ack_time_,
+ newly_acked_bytes);
+ // If |extra_acked| is zero, i.e. this ack event marks the start of a new ack
+ // aggregation epoch, save LessRecentPoint, which is the last ack point of the
+ // previous epoch, as a A0 candidate.
+ if (overestimate_avoidance_ && extra_acked == 0) {
+ a0_candidates_.push_back(recent_ack_points_.LessRecentPoint());
+ QUIC_DVLOG(1) << "New a0_candidate:" << a0_candidates_.back();
+ }
+ return extra_acked;
+}
+
+BandwidthSample BandwidthSampler::OnPacketAcknowledged(
+ QuicTime ack_time, QuicPacketNumber packet_number) {
+ last_acked_packet_ = packet_number;
+ ConnectionStateOnSentPacket* sent_packet_pointer =
+ connection_state_map_.GetEntry(packet_number);
+ if (sent_packet_pointer == nullptr) {
+ // See the TODO below.
+ return BandwidthSample();
+ }
+ BandwidthSample sample =
+ OnPacketAcknowledgedInner(ack_time, packet_number, *sent_packet_pointer);
+ return sample;
+}
+
+BandwidthSample BandwidthSampler::OnPacketAcknowledgedInner(
+ QuicTime ack_time, QuicPacketNumber packet_number,
+ const ConnectionStateOnSentPacket& sent_packet) {
+ total_bytes_acked_ += sent_packet.size;
+ total_bytes_sent_at_last_acked_packet_ =
+ sent_packet.send_time_state.total_bytes_sent;
+ last_acked_packet_sent_time_ = sent_packet.sent_time;
+ last_acked_packet_ack_time_ = ack_time;
+ if (overestimate_avoidance_) {
+ recent_ack_points_.Update(ack_time, total_bytes_acked_);
+ }
+
+ if (is_app_limited_) {
+ // Exit app-limited phase in two cases:
+ // (1) end_of_app_limited_phase_ is not initialized, i.e., so far all
+ // packets are sent while there are buffered packets or pending data.
+ // (2) The current acked packet is after the sent packet marked as the end
+ // of the app limit phase.
+ if (!end_of_app_limited_phase_.IsInitialized() ||
+ packet_number > end_of_app_limited_phase_) {
+ is_app_limited_ = false;
+ }
+ }
+
+ // There might have been no packets acknowledged at the moment when the
+ // current packet was sent. In that case, there is no bandwidth sample to
+ // make.
+ if (sent_packet.last_acked_packet_sent_time == QuicTime::Zero()) {
+ QUIC_BUG(quic_bug_10437_4)
+ << "sent_packet.last_acked_packet_sent_time is zero";
+ return BandwidthSample();
+ }
+
+ // Infinite rate indicates that the sampler is supposed to discard the
+ // current send rate sample and use only the ack rate.
+ QuicBandwidth send_rate = QuicBandwidth::Infinite();
+ if (sent_packet.sent_time > sent_packet.last_acked_packet_sent_time) {
+ send_rate = QuicBandwidth::FromBytesAndTimeDelta(
+ sent_packet.send_time_state.total_bytes_sent -
+ sent_packet.total_bytes_sent_at_last_acked_packet,
+ sent_packet.sent_time - sent_packet.last_acked_packet_sent_time);
+ }
+
+ AckPoint a0;
+ if (overestimate_avoidance_ &&
+ ChooseA0Point(sent_packet.send_time_state.total_bytes_acked, &a0)) {
+ QUIC_DVLOG(2) << "Using a0 point: " << a0;
+ } else {
+ a0.ack_time = sent_packet.last_acked_packet_ack_time,
+ a0.total_bytes_acked = sent_packet.send_time_state.total_bytes_acked;
+ }
+
+ // During the slope calculation, ensure that ack time of the current packet is
+ // always larger than the time of the previous packet, otherwise division by
+ // zero or integer underflow can occur.
+ if (ack_time <= a0.ack_time) {
+ // TODO(wub): Compare this code count before and after fixing clock jitter
+ // issue.
+ if (a0.ack_time == sent_packet.sent_time) {
+ // This is the 1st packet after quiescense.
+ QUIC_CODE_COUNT_N(quic_prev_ack_time_larger_than_current_ack_time, 1, 2);
+ } else {
+ QUIC_CODE_COUNT_N(quic_prev_ack_time_larger_than_current_ack_time, 2, 2);
+ }
+ QUIC_LOG_EVERY_N_SEC(ERROR, 60)
+ << "Time of the previously acked packet:"
+ << a0.ack_time.ToDebuggingValue()
+ << " is larger than the ack time of the current packet:"
+ << ack_time.ToDebuggingValue()
+ << ". acked packet number:" << packet_number
+ << ", total_bytes_acked_:" << total_bytes_acked_
+ << ", overestimate_avoidance_:" << overestimate_avoidance_
+ << ", sent_packet:" << sent_packet;
+ return BandwidthSample();
+ }
+ QuicBandwidth ack_rate = QuicBandwidth::FromBytesAndTimeDelta(
+ total_bytes_acked_ - a0.total_bytes_acked, ack_time - a0.ack_time);
+
+ BandwidthSample sample;
+ sample.bandwidth = std::min(send_rate, ack_rate);
+ // Note: this sample does not account for delayed acknowledgement time. This
+ // means that the RTT measurements here can be artificially high, especially
+ // on low bandwidth connections.
+ sample.rtt = ack_time - sent_packet.sent_time;
+ sample.send_rate = send_rate;
+ SentPacketToSendTimeState(sent_packet, &sample.state_at_send);
+
+ if (sample.bandwidth.IsZero()) {
+ QUIC_LOG_EVERY_N_SEC(ERROR, 60)
+ << "ack_rate: " << ack_rate << ", send_rate: " << send_rate
+ << ". acked packet number:" << packet_number
+ << ", overestimate_avoidance_:" << overestimate_avoidance_ << "a1:{"
+ << total_bytes_acked_ << "@" << ack_time << "}, a0:{"
+ << a0.total_bytes_acked << "@" << a0.ack_time
+ << "}, sent_packet:" << sent_packet;
+ }
+ return sample;
+}
+
+bool BandwidthSampler::ChooseA0Point(QuicByteCount total_bytes_acked,
+ AckPoint* a0) {
+ if (a0_candidates_.empty()) {
+ QUIC_BUG(quic_bug_10437_5)
+ << "No A0 point candicates. total_bytes_acked:" << total_bytes_acked;
+ return false;
+ }
+
+ if (a0_candidates_.size() == 1) {
+ *a0 = a0_candidates_.front();
+ return true;
+ }
+
+ for (size_t i = 1; i < a0_candidates_.size(); ++i) {
+ if (a0_candidates_[i].total_bytes_acked > total_bytes_acked) {
+ *a0 = a0_candidates_[i - 1];
+ if (i > 1) {
+ a0_candidates_.pop_front_n(i - 1);
+ }
+ return true;
+ }
+ }
+
+ // All candidates' total_bytes_acked is <= |total_bytes_acked|.
+ *a0 = a0_candidates_.back();
+ a0_candidates_.pop_front_n(a0_candidates_.size() - 1);
+ return true;
+}
+
+SendTimeState BandwidthSampler::OnPacketLost(QuicPacketNumber packet_number,
+ QuicPacketLength bytes_lost) {
+ // TODO(vasilvv): see the comment for the case of missing packets in
+ // BandwidthSampler::OnPacketAcknowledged on why this does not raise a
+ // QUIC_BUG when removal fails.
+ SendTimeState send_time_state;
+
+ total_bytes_lost_ += bytes_lost;
+ ConnectionStateOnSentPacket* sent_packet_pointer =
+ connection_state_map_.GetEntry(packet_number);
+ if (sent_packet_pointer != nullptr) {
+ SentPacketToSendTimeState(*sent_packet_pointer, &send_time_state);
+ }
+
+ return send_time_state;
+}
+
+void BandwidthSampler::SentPacketToSendTimeState(
+ const ConnectionStateOnSentPacket& sent_packet,
+ SendTimeState* send_time_state) const {
+ *send_time_state = sent_packet.send_time_state;
+ send_time_state->is_valid = true;
+}
+
+void BandwidthSampler::OnAppLimited() {
+ is_app_limited_ = true;
+ end_of_app_limited_phase_ = last_sent_packet_;
+}
+
+void BandwidthSampler::RemoveObsoletePackets(QuicPacketNumber least_unacked) {
+ // A packet can become obsolete when it is removed from QuicUnackedPacketMap's
+ // view of inflight before it is acked or marked as lost. For example, when
+ // QuicSentPacketManager::RetransmitCryptoPackets retransmits a crypto packet,
+ // the packet is removed from QuicUnackedPacketMap's inflight, but is not
+ // marked as acked or lost in the BandwidthSampler.
+ connection_state_map_.RemoveUpTo(least_unacked);
+}
+
+QuicByteCount BandwidthSampler::total_bytes_sent() const {
+ return total_bytes_sent_;
+}
+
+QuicByteCount BandwidthSampler::total_bytes_acked() const {
+ return total_bytes_acked_;
+}
+
+QuicByteCount BandwidthSampler::total_bytes_lost() const {
+ return total_bytes_lost_;
+}
+
+QuicByteCount BandwidthSampler::total_bytes_neutered() const {
+ return total_bytes_neutered_;
+}
+
+bool BandwidthSampler::is_app_limited() const { return is_app_limited_; }
+
+QuicPacketNumber BandwidthSampler::end_of_app_limited_phase() const {
+ return end_of_app_limited_phase_;
+}
+
+} // namespace quic