blob: 85fa62bdfbeaf341292f500c5125557dc79ce716 [file] [log] [blame]
// Copyright (c) 2012 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 "net/quic/congestion_control/quic_send_scheduler.h"
#include <algorithm>
#include <cmath>
#include <map>
#include "base/stl_util.h"
#include "base/time.h"
#include "net/quic/congestion_control/send_algorithm_interface.h"
//#include "util/gtl/map-util.h"
using std::map;
using std::max;
namespace net {
const int64 kNumMicrosPerSecond = base::Time::kMicrosecondsPerSecond;
QuicSendScheduler::QuicSendScheduler(
const QuicClock* clock,
CongestionFeedbackType type)
: clock_(clock),
current_estimated_bandwidth_(-1),
max_estimated_bandwidth_(-1),
last_sent_packet_(QuicTime::FromMicroseconds(0)),
current_packet_bucket_(-1),
first_packet_bucket_(-1),
send_algorithm_(SendAlgorithmInterface::Create(clock, type)) {
memset(packet_history_, 0, sizeof(packet_history_));
}
QuicSendScheduler::~QuicSendScheduler() {
STLDeleteContainerPairSecondPointers(pending_packets_.begin(),
pending_packets_.end());
}
int QuicSendScheduler::UpdatePacketHistory() {
int timestamp_scaled = clock_->Now().ToMicroseconds() /
kBitrateSmoothingPeriod;
int bucket = timestamp_scaled % kBitrateSmoothingBuckets;
if (!HasSentPacket()) {
// First packet.
current_packet_bucket_ = bucket;
first_packet_bucket_ = bucket;
}
if (current_packet_bucket_ != bucket) {
// We need to make sure to zero out any skipped buckets.
// Max loop count is kBitrateSmoothingBuckets.
do {
current_packet_bucket_ =
(1 + current_packet_bucket_) % kBitrateSmoothingBuckets;
packet_history_[current_packet_bucket_] = 0;
if (first_packet_bucket_ == current_packet_bucket_) {
// We have filled the whole window, no need to keep track of first
// bucket.
first_packet_bucket_ = -1;
}
} while (current_packet_bucket_ != bucket);
}
return bucket;
}
void QuicSendScheduler::SentPacket(QuicPacketSequenceNumber sequence_number,
size_t bytes,
bool retransmit) {
int bucket = UpdatePacketHistory();
packet_history_[bucket] += bytes;
send_algorithm_->SentPacket(sequence_number, bytes, retransmit);
if (!retransmit) {
pending_packets_[sequence_number] =
new PendingPacket(bytes, clock_->Now());
}
DVLOG(1) << "Sent sequence number:" << sequence_number;
}
void QuicSendScheduler::OnIncomingQuicCongestionFeedbackFrame(
const QuicCongestionFeedbackFrame& congestion_feedback_frame) {
send_algorithm_->OnIncomingQuicCongestionFeedbackFrame(
congestion_feedback_frame);
}
void QuicSendScheduler::OnIncomingAckFrame(const QuicAckFrame& ack_frame) {
// We want to.
// * Get all packets lower(including) than largest_received
// from pending_packets_.
// * Remove all missing packets.
// * Send each ACK in the list to send_algorithm_.
QuicTime last_timestamp(QuicTime::FromMicroseconds(0));
map<QuicPacketSequenceNumber, size_t> acked_packets;
PendingPacketsMap::iterator it, it_upper;
it = pending_packets_.begin();
it_upper = pending_packets_.upper_bound(
ack_frame.received_info.largest_received);
while (it != it_upper) {
QuicPacketSequenceNumber sequence_number = it->first;
if (!ack_frame.received_info.IsAwaitingPacket(sequence_number)) {
// Not missing, hence implicitly acked.
scoped_ptr<PendingPacket> pending_packet_cleaner(it->second);
acked_packets[sequence_number] = pending_packet_cleaner->BytesSent();
last_timestamp = pending_packet_cleaner->SendTimestamp();
pending_packets_.erase(it++); // Must be incremented post to work.
} else {
++it;
}
}
// We calculate the RTT based on the highest ACKed sequence number, the lower
// sequence numbers will include the ACK aggregation delay.
QuicTime::Delta rtt = clock_->Now().Subtract(last_timestamp);
map<QuicPacketSequenceNumber, size_t>::iterator it_acked_packets;
for (it_acked_packets = acked_packets.begin();
it_acked_packets != acked_packets.end();
++it_acked_packets) {
send_algorithm_->OnIncomingAck(it_acked_packets->first,
it_acked_packets->second,
rtt);
DVLOG(1) << "ACKed sequence number:" << it_acked_packets->first;
}
}
QuicTime::Delta QuicSendScheduler::TimeUntilSend(bool retransmit) {
return send_algorithm_->TimeUntilSend(retransmit);
}
size_t QuicSendScheduler::AvailableCongestionWindow() {
return send_algorithm_->AvailableCongestionWindow();
}
int QuicSendScheduler::BandwidthEstimate() {
int bandwidth_estimate = send_algorithm_->BandwidthEstimate();
if (bandwidth_estimate == kNoValidEstimate) {
// If we don't have a valid estimate use the send rate.
return SentBandwidth();
}
return bandwidth_estimate;
}
bool QuicSendScheduler::HasSentPacket() {
return (current_packet_bucket_ != -1);
}
// TODO(pwestin) add a timer to make this accurate even if not called.
int QuicSendScheduler::SentBandwidth() {
UpdatePacketHistory();
if (first_packet_bucket_ != -1) {
// We don't have a full set of data.
int number_of_buckets = (current_packet_bucket_ - first_packet_bucket_) + 1;
if (number_of_buckets < 0) {
// We have a wrap in bucket index.
number_of_buckets = kBitrateSmoothingBuckets + number_of_buckets;
}
int64 sum = 0;
int bucket = first_packet_bucket_;
for (int n = 0; n < number_of_buckets; bucket++, n++) {
bucket = bucket % kBitrateSmoothingBuckets;
sum += packet_history_[bucket];
}
current_estimated_bandwidth_ = (sum *
(kNumMicrosPerSecond / kBitrateSmoothingPeriod)) / number_of_buckets;
} else {
int64 sum = 0;
for (uint32 bucket = 0; bucket < kBitrateSmoothingBuckets; ++bucket) {
sum += packet_history_[bucket];
}
current_estimated_bandwidth_ = (sum * (kNumMicrosPerSecond /
kBitrateSmoothingPeriod)) / kBitrateSmoothingBuckets;
}
max_estimated_bandwidth_ = max(max_estimated_bandwidth_,
current_estimated_bandwidth_);
return current_estimated_bandwidth_;
}
int QuicSendScheduler::PeakSustainedBandwidth() {
// To make sure that we get the latest estimate we call SentBandwidth.
if (HasSentPacket()) {
SentBandwidth();
}
return max_estimated_bandwidth_;
}
} // namespace net