Merge QUIC InterArrival congestion control code to chromium.
Review URL: https://chromiumcodereview.appspot.com/13660002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@193483 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/net/quic/congestion_control/available_channel_estimator.cc b/net/quic/congestion_control/available_channel_estimator.cc
new file mode 100644
index 0000000..ef091ab
--- /dev/null
+++ b/net/quic/congestion_control/available_channel_estimator.cc
@@ -0,0 +1,78 @@
+// 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 "net/quic/congestion_control/available_channel_estimator.h"
+
+static const int kNumberOfSamples = 9;
+
+namespace net {
+
+AvailableChannelEstimator::AvailableChannelEstimator(
+ QuicPacketSequenceNumber sequence_number,
+ QuicTime first_send_time,
+ QuicTime first_receive_time)
+ : first_sequence_number_(sequence_number),
+ first_send_time_(first_send_time),
+ first_receive_time_(first_receive_time),
+ last_incorporated_sequence_number_(sequence_number),
+ last_time_sent_(QuicTime::Zero()),
+ last_receive_time_(QuicTime::Zero()),
+ number_of_sequence_numbers_(0),
+ received_bytes_(0) {
+}
+
+void AvailableChannelEstimator::OnIncomingFeedback(
+ QuicPacketSequenceNumber sequence_number,
+ QuicByteCount packet_size,
+ QuicTime sent_time,
+ QuicTime receive_time) {
+ if (sequence_number <= first_sequence_number_) {
+ // Ignore pre-probe feedback.
+ return;
+ }
+ if (sequence_number <= last_incorporated_sequence_number_) {
+ // Ignore old feedback; will remove duplicates.
+ return;
+ }
+ // Remember the highest received sequence number.
+ last_incorporated_sequence_number_ = sequence_number;
+ if (number_of_sequence_numbers_ < kNumberOfSamples) {
+ // We don't care how many sequence numbers we have after we pass
+ // kNumberOfSamples.
+ number_of_sequence_numbers_++;
+ }
+ last_receive_time_ = receive_time;
+ last_time_sent_ = sent_time;
+ received_bytes_ += packet_size;
+ // TODO(pwestin): the variance here should give us information about accuracy.
+}
+
+AvailableChannelEstimateState
+ AvailableChannelEstimator::GetAvailableChannelEstimate(
+ QuicBandwidth* bandwidth) const {
+ if (number_of_sequence_numbers_ < 2) {
+ return kAvailableChannelEstimateUnknown;
+ }
+ QuicTime::Delta send_delta = last_time_sent_.Subtract(first_send_time_);
+ QuicTime::Delta receive_delta =
+ last_receive_time_.Subtract(first_receive_time_);
+
+ // TODO(pwestin): room for improvement here. Keeping it simple for now.
+ *bandwidth = QuicBandwidth::FromBytesAndTimeDelta(received_bytes_,
+ receive_delta);
+
+ QuicTime::Delta diff = receive_delta.Subtract(send_delta);
+ QuicTime::Delta ten_percent_of_send_time =
+ QuicTime::Delta::FromMicroseconds(send_delta.ToMicroseconds() / 10);
+
+ if (diff < ten_percent_of_send_time) {
+ return kAvailableChannelEstimateSenderLimited;
+ }
+ if (number_of_sequence_numbers_ < kNumberOfSamples) {
+ return kAvailableChannelEstimateUncertain;
+ }
+ return kAvailableChannelEstimateGood;
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/available_channel_estimator.h b/net/quic/congestion_control/available_channel_estimator.h
new file mode 100644
index 0000000..e2ad19a
--- /dev/null
+++ b/net/quic/congestion_control/available_channel_estimator.h
@@ -0,0 +1,64 @@
+// 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.
+
+// Based on the inter arrival time of the received packets relative to the time
+// those packets where sent we can estimate the available capacity of the
+// channel.
+// We can only use packet trains that are sent out faster than the acctual
+// available channel capacity.
+// Note 1: this is intended to be a temporary class created when you send out a
+// channel probing burst. Once the last packet have arrived you ask it for an
+// estimate.
+// Note 2: During this phase we should not update the overuse detector.
+#ifndef NET_QUIC_CONGESTION_CONTROL_AVAILABLE_CHANNEL_ESTIMATOR_H_
+#define NET_QUIC_CONGESTION_CONTROL_AVAILABLE_CHANNEL_ESTIMATOR_H_
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_bandwidth.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+enum NET_EXPORT_PRIVATE AvailableChannelEstimateState {
+ kAvailableChannelEstimateUnknown = 0,
+ kAvailableChannelEstimateUncertain = 1,
+ kAvailableChannelEstimateGood = 2,
+ kAvailableChannelEstimateSenderLimited = 3,
+};
+
+class NET_EXPORT_PRIVATE AvailableChannelEstimator {
+ public:
+ explicit AvailableChannelEstimator(
+ QuicPacketSequenceNumber first_sequence_number,
+ QuicTime first_send_time,
+ QuicTime first_receive_time);
+
+ // Update the statistics with each receive time, for every packet we get a
+ // feedback message for.
+ void OnIncomingFeedback(QuicPacketSequenceNumber sequence_number,
+ QuicByteCount packet_size,
+ QuicTime sent_time,
+ QuicTime receive_time);
+
+ // Get the current estimated available channel capacity.
+ // bandwidth_estimate is invalid if kAvailableChannelEstimateUnknown
+ // is returned.
+ AvailableChannelEstimateState GetAvailableChannelEstimate(
+ QuicBandwidth* bandwidth_estimate) const;
+
+ private:
+ const QuicPacketSequenceNumber first_sequence_number_;
+ const QuicTime first_send_time_;
+ const QuicTime first_receive_time_;
+ QuicPacketSequenceNumber last_incorporated_sequence_number_;
+ QuicTime last_time_sent_;
+ QuicTime last_receive_time_;
+ int number_of_sequence_numbers_;
+ QuicByteCount received_bytes_;
+};
+
+} // namespace net
+#endif // NET_QUIC_CONGESTION_CONTROL_AVAILABLE_CHANNEL_ESTIMATOR_H_
diff --git a/net/quic/congestion_control/available_channel_estimator_test.cc b/net/quic/congestion_control/available_channel_estimator_test.cc
new file mode 100644
index 0000000..8c1c69e9
--- /dev/null
+++ b/net/quic/congestion_control/available_channel_estimator_test.cc
@@ -0,0 +1,109 @@
+// 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 "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/congestion_control/available_channel_estimator.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class AvailableChannelEstimatorTest : public ::testing::Test {
+ protected:
+ void SetUp() {
+ srand(1234);
+ packet_size_ = 1200;
+ sequence_number_ = 1;
+ QuicTime receive_time = receive_clock_.Now();
+ QuicTime sent_time = send_clock_.Now();
+ estimator_.reset(new AvailableChannelEstimator(sequence_number_,
+ sent_time,
+ receive_time));
+ }
+
+ MockClock send_clock_;
+ MockClock receive_clock_;
+ QuicPacketSequenceNumber sequence_number_;
+ QuicByteCount packet_size_;
+ scoped_ptr<AvailableChannelEstimator> estimator_;
+};
+
+TEST_F(AvailableChannelEstimatorTest, SimpleBasic) {
+ QuicBandwidth bandwidth = QuicBandwidth::Zero();
+ QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10);
+ QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(1);
+ receive_clock_.AdvanceTime(received_delta);
+ send_clock_.AdvanceTime(send_delta);
+ QuicTime receive_time = receive_clock_.Now();
+ QuicTime sent_time = send_clock_.Now();
+ estimator_->OnIncomingFeedback(++sequence_number_,
+ packet_size_,
+ sent_time,
+ receive_time);
+ EXPECT_EQ(kAvailableChannelEstimateUnknown,
+ estimator_->GetAvailableChannelEstimate(&bandwidth));
+
+ receive_clock_.AdvanceTime(received_delta);
+ receive_time = receive_clock_.Now();
+ send_clock_.AdvanceTime(send_delta);
+ sent_time = send_clock_.Now();
+
+ estimator_->OnIncomingFeedback(++sequence_number_,
+ packet_size_,
+ sent_time,
+ receive_time);
+ EXPECT_EQ(kAvailableChannelEstimateUncertain,
+ estimator_->GetAvailableChannelEstimate(&bandwidth));
+
+ EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta),
+ bandwidth);
+}
+
+// TODO(pwestin): simulate cross traffic.
+TEST_F(AvailableChannelEstimatorTest, SimpleUncertainEstimate) {
+ QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10);
+ QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(1);
+
+ for (int i = 0; i < 8; ++i) {
+ receive_clock_.AdvanceTime(received_delta);
+ QuicTime receive_time = receive_clock_.Now();
+ send_clock_.AdvanceTime(send_delta);
+ QuicTime sent_time = send_clock_.Now();
+ estimator_->OnIncomingFeedback(++sequence_number_,
+ packet_size_,
+ sent_time,
+ receive_time);
+ }
+ QuicBandwidth bandwidth = QuicBandwidth::Zero();
+ EXPECT_EQ(kAvailableChannelEstimateUncertain,
+ estimator_->GetAvailableChannelEstimate(&bandwidth));
+ EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta),
+ bandwidth);
+}
+
+TEST_F(AvailableChannelEstimatorTest, SimpleGoodEstimate) {
+ QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10);
+ QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(1);
+
+ for (int i = 0; i < 100; ++i) {
+ receive_clock_.AdvanceTime(received_delta);
+ QuicTime receive_time = receive_clock_.Now();
+ send_clock_.AdvanceTime(send_delta);
+ QuicTime sent_time = send_clock_.Now();
+ estimator_->OnIncomingFeedback(++sequence_number_,
+ packet_size_,
+ sent_time,
+ receive_time);
+ }
+ QuicBandwidth bandwidth = QuicBandwidth::Zero();
+ EXPECT_EQ(kAvailableChannelEstimateGood,
+ estimator_->GetAvailableChannelEstimate(&bandwidth));
+ EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta),
+ bandwidth);
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/congestion_control/channel_estimator.cc b/net/quic/congestion_control/channel_estimator.cc
new file mode 100644
index 0000000..54b96ae
--- /dev/null
+++ b/net/quic/congestion_control/channel_estimator.cc
@@ -0,0 +1,110 @@
+// 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 "net/quic/congestion_control/channel_estimator.h"
+
+// To get information about bandwidth, our send rate for a pair of packets must
+// be much faster (ideally back to back) than the receive rate. In that
+// scenario, the arriving packet pair will tend to arrive at the max bandwidth
+// of the channel. Said another way, when our inter-departure time is a small
+// fraction of the inter-arrival time for the same pair of packets, then we can
+// get an estimate of bandwidth from that interarrival time. The following
+// constant is the threshold ratio for deriving bandwidth information.
+static const int kInterarrivalRatioThresholdForBandwidthEstimation = 5;
+static const size_t kMinNumberOfSamples = 10;
+static const size_t kMaxNumberOfSamples = 100;
+
+namespace net {
+
+ChannelEstimator::ChannelEstimator()
+ : last_sequence_number_(0),
+ last_send_time_(QuicTime::Zero()),
+ last_receive_time_(QuicTime::Zero()),
+ sorted_bitrate_estimates_(kMaxNumberOfSamples) {
+}
+
+ChannelEstimator::~ChannelEstimator() {
+}
+
+void ChannelEstimator::OnAcknowledgedPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicByteCount packet_size,
+ QuicTime send_time,
+ QuicTime receive_time) {
+ if (last_sequence_number_ > sequence_number) {
+ // Old packet. The sequence_number use the full 64 bits even though it's
+ // less on the wire.
+ return;
+ }
+ if (last_sequence_number_ != sequence_number - 1) {
+ DLOG(INFO) << "Skip channel estimator due to lost packet(s)";
+ } else if (last_send_time_.IsInitialized()) {
+ QuicTime::Delta sent_delta = send_time.Subtract(last_send_time_);
+ QuicTime::Delta received_delta = receive_time.Subtract(last_receive_time_);
+ if (received_delta.ToMicroseconds() >
+ kInterarrivalRatioThresholdForBandwidthEstimation *
+ sent_delta.ToMicroseconds()) {
+ UpdateFilter(received_delta, packet_size, sequence_number);
+ }
+ }
+ last_sequence_number_ = sequence_number;
+ last_send_time_ = send_time;
+ last_receive_time_ = receive_time;
+}
+
+ChannelEstimateState ChannelEstimator::GetChannelEstimate(
+ QuicBandwidth* estimate) const {
+ if (sorted_bitrate_estimates_.Size() < kMinNumberOfSamples) {
+ // Not enough data to make an estimate.
+ return kChannelEstimateUnknown;
+ }
+ // Our data is stored in a sorted map, we need to iterate through our map to
+ // find the estimated bitrates at our targeted percentiles.
+
+ // Calculate 25th percentile.
+ size_t beginning_window = sorted_bitrate_estimates_.Size() / 4;
+ // Calculate 50th percentile.
+ size_t median = sorted_bitrate_estimates_.Size() / 2;
+ // Calculate 75th percentile.
+ size_t end_window = sorted_bitrate_estimates_.Size() - beginning_window;
+
+ QuicBandwidth bitrate_25th_percentile = QuicBandwidth::Zero();
+ QuicBandwidth median_bitrate = QuicBandwidth::Zero();
+ QuicBandwidth bitrate_75th_percentile = QuicBandwidth::Zero();
+ QuicMaxSizedMap<QuicBandwidth, QuicPacketSequenceNumber>::ConstIterator it =
+ sorted_bitrate_estimates_.Begin();
+
+ for (size_t i = 0; i <= end_window; ++i, ++it) {
+ DCHECK(it != sorted_bitrate_estimates_.End());
+ if (i == beginning_window) {
+ bitrate_25th_percentile = it->first;
+ }
+ if (i == median) {
+ median_bitrate = it->first;
+ }
+ if (i == end_window) {
+ bitrate_75th_percentile = it->first;
+ }
+ }
+ *estimate = median_bitrate;
+ DLOG(INFO) << "Channel estimate is:"
+ << median_bitrate.ToKBitsPerSecond() << " Kbit/s";
+ // If the bitrates in our 25th to 75th percentile window varies more than
+ // 25% of the median bitrate we consider the estimate to be uncertain.
+ if (bitrate_75th_percentile.Subtract(bitrate_25th_percentile) >
+ median_bitrate.Scale(0.25f)) {
+ return kChannelEstimateUncertain;
+ }
+ return kChannelEstimateGood;
+}
+
+void ChannelEstimator::UpdateFilter(QuicTime::Delta received_delta,
+ QuicByteCount size_delta,
+ QuicPacketSequenceNumber sequence_number) {
+ QuicBandwidth estimate =
+ QuicBandwidth::FromBytesAndTimeDelta(size_delta, received_delta);
+ sorted_bitrate_estimates_.Insert(estimate, sequence_number);
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/channel_estimator.h b/net/quic/congestion_control/channel_estimator.h
new file mode 100644
index 0000000..9be8031
--- /dev/null
+++ b/net/quic/congestion_control/channel_estimator.h
@@ -0,0 +1,61 @@
+// 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.
+
+// Based on the inter arrival time of the received packets relative to the time
+// those packets where sent we can estimate the max capacity of the channel.
+// We can only use packet pair that are sent out faster than the acctual
+// channel capacity.
+#ifndef NET_QUIC_CONGESTION_CONTROL_CHANNEL_ESTIMATOR_H_
+#define NET_QUIC_CONGESTION_CONTROL_CHANNEL_ESTIMATOR_H_
+
+#include <list>
+#include <map>
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+#include "net/quic/congestion_control/quic_max_sized_map.h"
+#include "net/quic/quic_bandwidth.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+enum ChannelEstimateState {
+ kChannelEstimateUnknown = 0,
+ kChannelEstimateUncertain = 1,
+ kChannelEstimateGood = 2
+};
+
+class NET_EXPORT_PRIVATE ChannelEstimator {
+ public:
+ ChannelEstimator();
+ ~ChannelEstimator();
+
+ // This method should be called each time we acquire a receive time for a
+ // packet we previously sent. It calculates deltas between consecutive
+ // receive times, and may use that to update the channel bandwidth estimate.
+ void OnAcknowledgedPacket(QuicPacketSequenceNumber sequence_number,
+ QuicByteCount packet_size,
+ QuicTime send_time,
+ QuicTime receive_time);
+
+ // Get the current estimated state and channel capacity.
+ // Note: estimate will not be valid when kChannelEstimateUnknown is returned.
+ ChannelEstimateState GetChannelEstimate(QuicBandwidth* estimate) const;
+
+ private:
+ void UpdateFilter(QuicTime::Delta received_delta, QuicByteCount size_delta,
+ QuicPacketSequenceNumber sequence_number);
+
+ QuicPacketSequenceNumber last_sequence_number_;
+ QuicTime last_send_time_;
+ QuicTime last_receive_time_;
+ QuicMaxSizedMap<QuicBandwidth, QuicPacketSequenceNumber>
+ sorted_bitrate_estimates_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChannelEstimator);
+};
+
+} // namespace net
+#endif // NET_QUIC_CONGESTION_CONTROL_CHANNEL_ESTIMATOR_H_
diff --git a/net/quic/congestion_control/channel_estimator_test.cc b/net/quic/congestion_control/channel_estimator_test.cc
new file mode 100644
index 0000000..7ff1b6c2
--- /dev/null
+++ b/net/quic/congestion_control/channel_estimator_test.cc
@@ -0,0 +1,224 @@
+// 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 "base/logging.h"
+#include "net/quic/congestion_control/channel_estimator.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class ChannelEstimatorTest : public ::testing::Test {
+ protected:
+ void SetUp() {
+ srand(1234);
+ packet_size_ = 1200;
+ sequence_number_ = 1;
+ }
+
+ QuicPacketSequenceNumber sequence_number_;
+ QuicByteCount packet_size_;
+ MockClock send_clock_;
+ MockClock receive_clock_;
+ ChannelEstimator channel_estimator_;
+};
+
+TEST_F(ChannelEstimatorTest, SimpleNonDetect) {
+ // In this test, the send times differ by the same delta as the receive times,
+ // so we haven't sent packets closely enough to detect "spreading," or
+ // effective bandwidth.
+ QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(10);
+
+ for (int i = 0; i < 1000; ++i) {
+ QuicTime send_time = send_clock_.ApproximateNow();
+ QuicTime receive_time = receive_clock_.ApproximateNow();
+ channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
+ packet_size_,
+ send_time,
+ receive_time);
+ send_clock_.AdvanceTime(delta);
+ receive_clock_.AdvanceTime(delta);
+ }
+ QuicBandwidth estimate = QuicBandwidth::Zero();
+ EXPECT_EQ(kChannelEstimateUnknown,
+ channel_estimator_.GetChannelEstimate(&estimate));
+ EXPECT_TRUE(estimate.IsZero());
+}
+
+TEST_F(ChannelEstimatorTest, SimplePacketPairDetect) {
+ // In this test, we start by sending packet pairs back-to-back and
+ // add a receive side spreading that indicate an effective bandwidth.
+ // We do 2 testes with different effective bandwidth to make sure that we
+ // detect the new effective bandwidth.
+ QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(5);
+ QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10);
+
+ for (int i = 0; i < 100; ++i) {
+ receive_clock_.AdvanceTime(received_delta);
+ QuicTime receive_time = receive_clock_.ApproximateNow();
+ QuicTime send_time = send_clock_.ApproximateNow();
+
+ channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
+ packet_size_,
+ send_time,
+ receive_time);
+ receive_clock_.AdvanceTime(received_delta);
+ receive_time = receive_clock_.ApproximateNow();
+ channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
+ packet_size_,
+ send_time,
+ receive_time);
+ send_clock_.AdvanceTime(send_delta);
+ }
+ QuicBandwidth estimate = QuicBandwidth::Zero();
+ EXPECT_EQ(kChannelEstimateGood,
+ channel_estimator_.GetChannelEstimate(&estimate));
+ EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta),
+ estimate);
+ received_delta = QuicTime::Delta::FromMilliseconds(1);
+ for (int i = 0; i < 100; ++i) {
+ receive_clock_.AdvanceTime(received_delta);
+ QuicTime receive_time = receive_clock_.ApproximateNow();
+ QuicTime send_time = send_clock_.ApproximateNow();
+ channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
+ packet_size_,
+ send_time,
+ receive_time);
+ receive_clock_.AdvanceTime(received_delta);
+ receive_time = receive_clock_.ApproximateNow();
+ channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
+ packet_size_,
+ send_time,
+ receive_time);
+ send_clock_.AdvanceTime(send_delta);
+ }
+ EXPECT_EQ(kChannelEstimateGood,
+ channel_estimator_.GetChannelEstimate(&estimate));
+ EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta),
+ estimate);
+}
+
+TEST_F(ChannelEstimatorTest, SimpleFlatSlope) {
+ // In this test, we send packet pairs back-to-back and add a slowly increasing
+ // receive side spreading. We expect the estimate to be good and that our
+ // mean receive side spreading is returned as the estimate.
+ QuicTime::Delta initial_received_delta = QuicTime::Delta::FromMilliseconds(5);
+ QuicTime::Delta received_delta = initial_received_delta;
+ QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10);
+
+ for (int i = 0; i < 100; ++i) {
+ receive_clock_.AdvanceTime(received_delta);
+ QuicTime receive_time = receive_clock_.ApproximateNow();
+ QuicTime send_time = send_clock_.ApproximateNow();
+ channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
+ packet_size_,
+ send_time,
+ receive_time);
+ receive_clock_.AdvanceTime(received_delta);
+ receive_time = receive_clock_.ApproximateNow();
+ channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
+ packet_size_,
+ send_time,
+ receive_time);
+ send_clock_.AdvanceTime(send_delta);
+ received_delta = received_delta.Add(QuicTime::Delta::FromMicroseconds(10));
+ }
+ QuicBandwidth estimate = QuicBandwidth::Zero();
+ EXPECT_EQ(kChannelEstimateGood,
+ channel_estimator_.GetChannelEstimate(&estimate));
+
+ // Calculate our mean receive delta.
+ QuicTime::Delta increased_received_delta =
+ received_delta.Subtract(initial_received_delta);
+ QuicTime::Delta mean_received_delta = initial_received_delta.Add(
+ QuicTime::Delta::FromMicroseconds(
+ increased_received_delta.ToMicroseconds() / 2));
+
+ EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_,
+ mean_received_delta), estimate);
+}
+
+TEST_F(ChannelEstimatorTest, SimpleMediumSlope) {
+ // In this test, we send packet pairs back-to-back and add an increasing
+ // receive side spreading. We expect the estimate to be uncertaint and that
+ // our mean receive side spreading is returned as the estimate.
+ QuicTime::Delta initial_received_delta = QuicTime::Delta::FromMilliseconds(5);
+ QuicTime::Delta received_delta = initial_received_delta;
+ QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10);
+
+ for (int i = 0; i < 100; ++i) {
+ receive_clock_.AdvanceTime(received_delta);
+ QuicTime receive_time = receive_clock_.ApproximateNow();
+ QuicTime send_time = send_clock_.ApproximateNow();
+ channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
+ packet_size_,
+ send_time,
+ receive_time);
+ receive_clock_.AdvanceTime(received_delta);
+ receive_time = receive_clock_.ApproximateNow();
+ channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
+ packet_size_,
+ send_time,
+ receive_time);
+ send_clock_.AdvanceTime(send_delta);
+ received_delta = received_delta.Add(QuicTime::Delta::FromMicroseconds(50));
+ }
+ QuicBandwidth estimate = QuicBandwidth::Zero();
+ EXPECT_EQ(kChannelEstimateUncertain,
+ channel_estimator_.GetChannelEstimate(&estimate));
+
+ // Calculate our mean receive delta.
+ QuicTime::Delta increased_received_delta =
+ received_delta.Subtract(initial_received_delta);
+ QuicTime::Delta mean_received_delta = initial_received_delta.Add(
+ QuicTime::Delta::FromMicroseconds(
+ increased_received_delta.ToMicroseconds() / 2));
+
+ EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_,
+ mean_received_delta), estimate);
+}
+
+TEST_F(ChannelEstimatorTest, SimpleSteepSlope) {
+ // In this test, we send packet pairs back-to-back and add a rapidly
+ // increasing receive side spreading. We expect the estimate to be uncertain
+ // and that our mean receive side spreading is returned as the estimate.
+ QuicTime::Delta initial_received_delta = QuicTime::Delta::FromMilliseconds(5);
+ QuicTime::Delta received_delta = initial_received_delta;
+ QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10);
+
+ for (int i = 0; i < 100; ++i) {
+ receive_clock_.AdvanceTime(received_delta);
+ QuicTime receive_time = receive_clock_.ApproximateNow();
+ QuicTime send_time = send_clock_.ApproximateNow();
+ channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
+ packet_size_,
+ send_time,
+ receive_time);
+ receive_clock_.AdvanceTime(received_delta);
+ receive_time = receive_clock_.ApproximateNow();
+ channel_estimator_.OnAcknowledgedPacket(sequence_number_++,
+ packet_size_,
+ send_time,
+ receive_time);
+ send_clock_.AdvanceTime(send_delta);
+ received_delta = received_delta.Add(QuicTime::Delta::FromMicroseconds(100));
+ }
+ QuicBandwidth estimate = QuicBandwidth::Zero();
+ EXPECT_EQ(kChannelEstimateUncertain,
+ channel_estimator_.GetChannelEstimate(&estimate));
+
+ // Calculate our mean receive delta.
+ QuicTime::Delta increased_received_delta =
+ received_delta.Subtract(initial_received_delta);
+ QuicTime::Delta mean_received_delta = initial_received_delta.Add(
+ QuicTime::Delta::FromMicroseconds(
+ increased_received_delta.ToMicroseconds() / 2));
+
+ EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_,
+ mean_received_delta), estimate);
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/congestion_control/cube_root.cc b/net/quic/congestion_control/cube_root.cc
new file mode 100644
index 0000000..c563ad9
--- /dev/null
+++ b/net/quic/congestion_control/cube_root.cc
@@ -0,0 +1,87 @@
+// 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 "net/quic/congestion_control/cube_root.h"
+
+#include "base/logging.h"
+
+namespace {
+
+// Find last bit in a 64-bit word.
+int FindMostSignificantBit(uint64 x) {
+ if (!x) {
+ return 0;
+ }
+ int r = 0;
+ if (x & 0xffffffff00000000ull) {
+ x >>= 32;
+ r += 32;
+ }
+ if (x & 0xffff0000u) {
+ x >>= 16;
+ r += 16;
+ }
+ if (x & 0xff00u) {
+ x >>= 8;
+ r += 8;
+ }
+ if (x & 0xf0u) {
+ x >>= 4;
+ r += 4;
+ }
+ if (x & 0xcu) {
+ x >>= 2;
+ r += 2;
+ }
+ if (x & 0x02u) {
+ x >>= 1;
+ r++;
+ }
+ if (x & 0x01u) {
+ r++;
+ }
+ return r;
+}
+
+// 6 bits table [0..63]
+const uint32 cube_root_table[] = {
+ 0, 54, 54, 54, 118, 118, 118, 118, 123, 129, 134, 138, 143, 147, 151,
+ 156, 157, 161, 164, 168, 170, 173, 176, 179, 181, 185, 187, 190, 192, 194,
+ 197, 199, 200, 202, 204, 206, 209, 211, 213, 215, 217, 219, 221, 222, 224,
+ 225, 227, 229, 231, 232, 234, 236, 237, 239, 240, 242, 244, 245, 246, 248,
+ 250, 251, 252, 254
+};
+} // namespace
+
+namespace net {
+
+// Calculate the cube root using a table lookup followed by one Newton-Raphson
+// iteration.
+uint32 CubeRoot::Root(uint64 a) {
+ uint32 msb = FindMostSignificantBit(a);
+ DCHECK_LE(msb, 64u);
+
+ if (msb < 7) {
+ // MSB in our table.
+ return ((cube_root_table[static_cast<uint32>(a)]) + 31) >> 6;
+ }
+ // MSB 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, ...
+ // cubic_shift 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, ...
+ uint32 cubic_shift = (msb - 4);
+ cubic_shift = ((cubic_shift * 342) >> 10); // Div by 3, biased high.
+
+ // 4 to 6 bits accuracy depending on MSB.
+ uint32 down_shifted_to_6bit = (a >> (cubic_shift * 3));
+ uint64 root = ((cube_root_table[down_shifted_to_6bit] + 10) << cubic_shift)
+ >> 6;
+
+ // Make one Newton-Raphson iteration.
+ // Since x has an error (inaccuracy due to the use of fix point) we get a
+ // more accurate result by doing x * (x - 1) instead of x * x.
+ root = 2 * root + (a / (root * (root - 1)));
+ root = ((root * 341) >> 10); // Div by 3, biased low.
+ return static_cast<uint32>(root);
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/cube_root.h b/net/quic/congestion_control/cube_root.h
new file mode 100644
index 0000000..3b3736c
--- /dev/null
+++ b/net/quic/congestion_control/cube_root.h
@@ -0,0 +1,21 @@
+// 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.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_CUBE_ROOT_H_
+#define NET_QUIC_CONGESTION_CONTROL_CUBE_ROOT_H_
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE CubeRoot {
+ public:
+ // Calculates the cube root using a table lookup followed by one Newton-
+ // Raphson iteration.
+ static uint32 Root(uint64 a);
+};
+
+} // namespace net
+#endif // NET_QUIC_CONGESTION_CONTROL_CUBE_ROOT_H_
diff --git a/net/quic/congestion_control/cube_root_test.cc b/net/quic/congestion_control/cube_root_test.cc
new file mode 100644
index 0000000..8f4729cb
--- /dev/null
+++ b/net/quic/congestion_control/cube_root_test.cc
@@ -0,0 +1,47 @@
+// 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 "base/basictypes.h"
+#include "net/quic/congestion_control/cube_root.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class CubeRootTest : public ::testing::Test {
+ protected:
+ CubeRootTest() {
+ }
+};
+
+TEST_F(CubeRootTest, LowRoot) {
+ for (uint32 i = 1; i < 256; ++i) {
+ uint64 cube = i * i * i;
+ uint8 cube_root = CubeRoot::Root(cube);
+ EXPECT_EQ(i, cube_root);
+ }
+}
+
+TEST_F(CubeRootTest, HighRoot) {
+ // Test the range we will opperate in, 1300 to 130 000.
+ // We expect some loss in accuracy, accepting +-0.2%.
+ for (uint64 i = 1300; i < 20000; i += 100) {
+ uint64 cube = i * i * i;
+ uint32 cube_root = CubeRoot::Root(cube);
+ uint32 margin = cube_root >> 9; // Calculate 0.2% roughly by
+ // dividing by 512.
+ EXPECT_LE(i - margin, cube_root);
+ EXPECT_GE(i + margin, cube_root);
+ }
+ for (uint64 i = 20000; i < 130000; i *= 2) {
+ uint64 cube = i * i * i;
+ uint32 cube_root = CubeRoot::Root(cube);
+ uint32 margin = cube_root >> 9;
+ EXPECT_LE(i - margin, cube_root);
+ EXPECT_GE(i + margin, cube_root);
+ }
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.cc b/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.cc
new file mode 100644
index 0000000..0a15a74d
--- /dev/null
+++ b/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.cc
@@ -0,0 +1,174 @@
+// 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 "net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "net/quic/congestion_control/cube_root.h"
+#include "net/quic/quic_protocol.h"
+
+namespace {
+// The following constants are in 2^10 fractions of a second instead of ms to
+// allow a 10 shift right to divide.
+const int kCubeScale = 40; // 1024*1024^3 (first 1024 is from 0.100^3)
+ // where 0.100 is 100 ms which is the scaling
+ // round trip time.
+// TODO(pwestin): Tuning parameter, currently close to TCP cubic at 100ms RTT.
+const int kPacedCubeScale = 6000;
+const uint64 kCubeFactor = (GG_UINT64_C(1) << kCubeScale) / kPacedCubeScale;
+} // namespace
+
+namespace net {
+
+InterArrivalBitrateRampUp::InterArrivalBitrateRampUp(const QuicClock* clock)
+ : clock_(clock),
+ current_rate_(QuicBandwidth::Zero()),
+ channel_estimate_(QuicBandwidth::Zero()),
+ available_channel_estimate_(QuicBandwidth::Zero()),
+ halfway_point_(QuicBandwidth::Zero()),
+ epoch_(QuicTime::Zero()),
+ last_update_time_(QuicTime::Zero()) {
+}
+
+void InterArrivalBitrateRampUp::Reset(QuicBandwidth new_rate,
+ QuicBandwidth available_channel_estimate,
+ QuicBandwidth channel_estimate) {
+ epoch_ = clock_->ApproximateNow();
+ last_update_time_ = epoch_;
+ available_channel_estimate_ = std::max(new_rate, available_channel_estimate);
+ channel_estimate_ = std::max(channel_estimate, available_channel_estimate_);
+
+ halfway_point_ = available_channel_estimate_.Add(
+ (channel_estimate_.Subtract(available_channel_estimate_)).Scale(0.5f));
+
+ if (new_rate < available_channel_estimate_) {
+ time_to_origin_point_ = CalcuateTimeToOriginPoint(
+ available_channel_estimate_.Subtract(new_rate));
+ } else if (new_rate >= channel_estimate_) {
+ time_to_origin_point_ = 0;
+ } else if (new_rate >= halfway_point_) {
+ time_to_origin_point_ =
+ CalcuateTimeToOriginPoint(channel_estimate_.Subtract(new_rate));
+ } else {
+ time_to_origin_point_ = CalcuateTimeToOriginPoint(
+ new_rate.Subtract(available_channel_estimate_));
+ }
+ current_rate_ = new_rate;
+ DLOG(INFO) << "Reset; time to origin point:" << time_to_origin_point_;
+}
+
+void InterArrivalBitrateRampUp::UpdateChannelEstimate(
+ QuicBandwidth channel_estimate) {
+ if (available_channel_estimate_ > channel_estimate ||
+ current_rate_ > channel_estimate ||
+ channel_estimate_ == channel_estimate) {
+ // Ignore, because one of the following reasons:
+ // 1) channel estimate is bellow our current available estimate which we
+ // value higher that this estimate.
+ // 2) channel estimate is bellow our current send rate.
+ // 3) channel estimate has not changed.
+ return;
+ }
+ if (available_channel_estimate_ == halfway_point_ &&
+ channel_estimate_ == halfway_point_) {
+ // First time we get a usable channel estimate.
+ channel_estimate_ = channel_estimate;
+ halfway_point_ = available_channel_estimate_.Add(
+ (channel_estimate_.Subtract(available_channel_estimate_).Scale(0.5f)));
+ DLOG(INFO) << "UpdateChannelEstimate; first usable value:"
+ << channel_estimate.ToKBitsPerSecond() << " Kbits/s";
+ return;
+ }
+ if (current_rate_ < halfway_point_) {
+ // Update channel estimate without recalculating if we are bellow the
+ // halfway point.
+ channel_estimate_ = channel_estimate;
+ return;
+ }
+ // We are between halfway point and our channel_estimate.
+ epoch_ = clock_->ApproximateNow();
+ last_update_time_ = epoch_;
+ channel_estimate_ = channel_estimate;
+
+ time_to_origin_point_ =
+ CalcuateTimeToOriginPoint(channel_estimate_.Subtract(current_rate_));
+
+ DLOG(INFO) << "UpdateChannelEstimate; time to origin point:"
+ << time_to_origin_point_;
+}
+
+QuicBandwidth InterArrivalBitrateRampUp::GetNewBitrate(
+ QuicBandwidth sent_bitrate) {
+ DCHECK(epoch_.IsInitialized());
+ QuicTime current_time = clock_->ApproximateNow();
+ // Cubic is "independent" of RTT, the update is limited by the time elapsed.
+ if (current_time.Subtract(last_update_time_) <= MaxCubicTimeInterval()) {
+ return current_rate_;
+ }
+ QuicTime::Delta time_from_last_update =
+ current_time.Subtract(last_update_time_);
+
+ last_update_time_ = current_time;
+
+ if (!sent_bitrate.IsZero() &&
+ sent_bitrate.Add(sent_bitrate) < current_rate_) {
+ // Don't go up in bitrate when we are not sending.
+ // We need to update the epoch to reflect this state.
+ epoch_ = epoch_.Add(time_from_last_update);
+ DLOG(INFO) << "Don't increase; our sent bitrate is:"
+ << sent_bitrate.ToKBitsPerSecond() << " Kbits/s"
+ << " current target rate is:"
+ << current_rate_.ToKBitsPerSecond() << " Kbits/s";
+ return current_rate_;
+ }
+ QuicTime::Delta time_from_epoch = current_time.Subtract(epoch_);
+
+ // Change the time unit from microseconds to 2^10 fractions per second. This
+ // is done to allow us to use shift as a divide operator.
+ int64 elapsed_time = (time_from_epoch.ToMicroseconds() << 10) /
+ kNumMicrosPerSecond;
+
+ int64 offset = time_to_origin_point_ - elapsed_time;
+ // Note: using int64 since QuicBandwidth can't be negative
+ int64 delta_pace_kbps = (kPacedCubeScale * offset * offset * offset) >>
+ kCubeScale;
+
+ bool start_bellow_halfway_point = false;
+ if (current_rate_ < halfway_point_) {
+ start_bellow_halfway_point = true;
+
+ // available_channel_estimate_ is the orgin of the cubic function.
+ QuicBandwidth current_rate = QuicBandwidth::FromBytesPerSecond(
+ available_channel_estimate_.ToBytesPerSecond() -
+ (delta_pace_kbps << 10));
+
+ if (start_bellow_halfway_point && current_rate >= halfway_point_) {
+ // We passed the halfway point, recalculate with new orgin.
+ epoch_ = clock_->ApproximateNow();
+ // channel_estimate_ is the new orgin of the cubic function.
+ if (current_rate >= channel_estimate_) {
+ time_to_origin_point_ = 0;
+ } else {
+ time_to_origin_point_ =
+ CalcuateTimeToOriginPoint(channel_estimate_.Subtract(current_rate));
+ }
+ DLOG(INFO) << "Passed the halfway point; time to origin point:"
+ << time_to_origin_point_;
+ }
+ current_rate_ = current_rate;
+ } else {
+ // channel_estimate_ is the orgin of the cubic function.
+ current_rate_ = QuicBandwidth::FromBytesPerSecond(
+ channel_estimate_.ToBytesPerSecond() - (delta_pace_kbps << 10));
+ }
+ return current_rate_;
+}
+
+uint32 InterArrivalBitrateRampUp::CalcuateTimeToOriginPoint(
+ QuicBandwidth rate_difference) const {
+ return CubeRoot::Root(kCubeFactor * rate_difference.ToKBytesPerSecond());
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h b/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h
new file mode 100644
index 0000000..93199239
--- /dev/null
+++ b/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h
@@ -0,0 +1,65 @@
+// 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.
+
+// Ramp up bitrate from a start point normally our "current_rate" as long as we
+// have no packet loss or delay events.
+// The first half of the ramp up curve follows a cubic function with its orgin
+// at the estimated available bandwidth, onece the bitrate pass the halfway
+// point between the estimated available bandwidth and the estimated max
+// bandwidth it will follw a new cubic function with its orgin at the estimated
+// max bandwidth.
+#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_BITRATE_RAMP_UP_H_
+#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_BITRATE_RAMP_UP_H_
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_bandwidth.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE InterArrivalBitrateRampUp {
+ public:
+ explicit InterArrivalBitrateRampUp(const QuicClock* clock);
+
+ // Call after a decision to lower the bitrate and after a probe.
+ void Reset(QuicBandwidth current_rate,
+ QuicBandwidth available_channel_estimate,
+ QuicBandwidth channel_estimate);
+
+ // Call everytime we get a new channel estimate.
+ void UpdateChannelEstimate(QuicBandwidth channel_estimate);
+
+ // Compute a new send pace to use.
+ QuicBandwidth GetNewBitrate(QuicBandwidth sent_bitrate);
+
+ private:
+ uint32 CalcuateTimeToOriginPoint(QuicBandwidth rate_difference) const;
+
+ static const QuicTime::Delta MaxCubicTimeInterval() {
+ return QuicTime::Delta::FromMilliseconds(30);
+ }
+
+ const QuicClock* clock_;
+
+ QuicBandwidth current_rate_;
+ QuicBandwidth channel_estimate_;
+ QuicBandwidth available_channel_estimate_;
+ QuicBandwidth halfway_point_;
+
+ // Time when this cycle started, after a Reset.
+ QuicTime epoch_;
+
+ // Time when we updated current_rate_.
+ QuicTime last_update_time_;
+
+ // Time to origin point of cubic function in 2^10 fractions of a second.
+ uint32 time_to_origin_point_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterArrivalBitrateRampUp);
+};
+
+} // namespace net
+#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_BITRATE_RAMP_UP_H_
diff --git a/net/quic/congestion_control/inter_arrival_bitrate_ramp_up_test.cc b/net/quic/congestion_control/inter_arrival_bitrate_ramp_up_test.cc
new file mode 100644
index 0000000..b644301
--- /dev/null
+++ b/net/quic/congestion_control/inter_arrival_bitrate_ramp_up_test.cc
@@ -0,0 +1,404 @@
+// 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 "base/basictypes.h"
+#include "base/logging.h"
+#include "net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class InterArrivalBitrateRampUpTest : public ::testing::Test {
+ protected:
+ InterArrivalBitrateRampUpTest()
+ : one_ms_(QuicTime::Delta::FromMilliseconds(1)),
+ hundred_ms_(QuicTime::Delta::FromMilliseconds(100)),
+ bitrate_ramp_up_(&clock_) {
+ }
+ void SetUp() {
+ clock_.AdvanceTime(one_ms_);
+ }
+ const QuicTime::Delta one_ms_;
+ const QuicTime::Delta hundred_ms_;
+ MockClock clock_;
+ InterArrivalBitrateRampUp bitrate_ramp_up_;
+};
+
+TEST_F(InterArrivalBitrateRampUpTest, GoodEstimates) {
+ QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100);
+ QuicBandwidth available_channel_estimate =
+ QuicBandwidth::FromKBytesPerSecond(200);
+ QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(400);
+ QuicBandwidth halfway_point = available_channel_estimate.Add(
+ channel_estimate.Subtract(available_channel_estimate).Scale(0.5f));
+ QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
+ bitrate_ramp_up_.Reset(start_rate,
+ available_channel_estimate,
+ channel_estimate);
+
+ // First concave growth, towards available_channel_estimate.
+ for (int i = 0; i < 25; ++i) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_GE(available_channel_estimate, sent_bitrate);
+ EXPECT_LE(start_rate, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(available_channel_estimate, sent_bitrate);
+
+ // First convex growth, from available_channel_estimate.
+ for (int j = 0; j < 25; ++j) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(available_channel_estimate, sent_bitrate);
+ EXPECT_GE(halfway_point, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(halfway_point, sent_bitrate);
+
+ // Second concave growth, towards channel_estimate.
+ for (int i = 0; i < 24; ++i) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_GE(channel_estimate, sent_bitrate);
+ EXPECT_LE(halfway_point, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(channel_estimate, sent_bitrate);
+
+ // Second convex growth, from channel_estimate.
+ for (int j = 0; j < 25; ++j) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(channel_estimate, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 100000,
+ sent_bitrate.ToBytesPerSecond(), 10000);
+
+ // Verify that we increase cubic.
+ for (int j = 0; j < 23; ++j) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(channel_estimate, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 750000,
+ sent_bitrate.ToBytesPerSecond(), 10000);
+}
+
+TEST_F(InterArrivalBitrateRampUpTest, GoodEstimatesLimitedSendRate) {
+ QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100);
+ QuicBandwidth available_channel_estimate =
+ QuicBandwidth::FromKBytesPerSecond(200);
+ QuicBandwidth max_sent_rate =
+ QuicBandwidth::FromKBytesPerSecond(125);
+ QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(400);
+ QuicBandwidth halfway_point = available_channel_estimate.Add(
+ channel_estimate.Subtract(available_channel_estimate).Scale(0.5f));
+ QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
+ bitrate_ramp_up_.Reset(start_rate,
+ available_channel_estimate,
+ channel_estimate);
+
+ // First concave growth, towards available_channel_estimate.
+ // Should pass without being affected by the max_sent_rate.
+ for (int i = 0; i < 25; ++i) {
+ clock_.AdvanceTime(hundred_ms_);
+ // Cap our previus sent rate.
+ sent_bitrate = std::min(sent_bitrate, max_sent_rate);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_GE(available_channel_estimate, sent_bitrate);
+ EXPECT_LE(start_rate, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ // Cap our previus sent rate.
+ sent_bitrate = std::min(sent_bitrate, max_sent_rate);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(available_channel_estimate, sent_bitrate);
+
+ // First convex growth, from available_channel_estimate.
+ for (int j = 0; j < 25; ++j) {
+ clock_.AdvanceTime(hundred_ms_);
+ // Cap our previus sent rate.
+ sent_bitrate = std::min(sent_bitrate, max_sent_rate);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(available_channel_estimate, sent_bitrate);
+ EXPECT_GE(halfway_point, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = std::min(sent_bitrate, max_sent_rate);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ // We expect 2 * sent_bitrate to cap the rate.
+ EXPECT_LE(max_sent_rate.Add(max_sent_rate), sent_bitrate);
+ // Remove our sent cap.
+ // Expect bitrate to continue to ramp from its previous rate.
+ for (int j = 0; j < 5; ++j) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(available_channel_estimate, sent_bitrate);
+ EXPECT_LE(max_sent_rate.Add(max_sent_rate), sent_bitrate);
+ EXPECT_GE(halfway_point, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(halfway_point, sent_bitrate);
+}
+
+TEST_F(InterArrivalBitrateRampUpTest, GoodEstimatesCloseToChannelEstimate) {
+ QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100);
+ QuicBandwidth available_channel_estimate =
+ QuicBandwidth::FromKBytesPerSecond(200);
+ QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(250);
+ QuicBandwidth halfway_point = available_channel_estimate.Add(
+ channel_estimate.Subtract(available_channel_estimate).Scale(0.5f));
+ QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
+ bitrate_ramp_up_.Reset(start_rate,
+ available_channel_estimate,
+ channel_estimate);
+
+ // First concave growth, towards available_channel_estimate.
+ for (int i = 0; i < 25; ++i) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_GE(available_channel_estimate, sent_bitrate);
+ EXPECT_LE(start_rate, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(available_channel_estimate, sent_bitrate);
+
+ // First convex growth, from available_channel_estimate.
+ for (int j = 0; j < 15; ++j) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(available_channel_estimate, sent_bitrate);
+ EXPECT_GE(halfway_point, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(halfway_point, sent_bitrate);
+
+ // Second concave growth, towards channel_estimate.
+ for (int i = 0; i < 14; ++i) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_GE(channel_estimate, sent_bitrate);
+ EXPECT_LE(halfway_point, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(channel_estimate, sent_bitrate);
+
+ // Second convex growth, from channel_estimate.
+ for (int j = 0; j < 25; ++j) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(channel_estimate, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 100000,
+ sent_bitrate.ToBytesPerSecond(), 10000);
+
+ // Verify that we increase cubic.
+ for (int j = 0; j < 24; ++j) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(channel_estimate, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 780000,
+ sent_bitrate.ToBytesPerSecond(), 20000);
+}
+
+TEST_F(InterArrivalBitrateRampUpTest, UncertainEstimates) {
+ QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100);
+ QuicBandwidth available_channel_estimate =
+ QuicBandwidth::FromKBytesPerSecond(200);
+ QuicBandwidth channel_estimate =
+ QuicBandwidth::FromKBytesPerSecond(400 * 0.7f);
+ QuicBandwidth halfway_point = available_channel_estimate.Add(
+ channel_estimate.Subtract(available_channel_estimate).Scale(0.5f));
+ QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
+ bitrate_ramp_up_.Reset(start_rate,
+ available_channel_estimate,
+ channel_estimate);
+
+ // First concave growth, towards available_channel_estimate.
+ for (int i = 0; i < 20; ++i) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_GE(available_channel_estimate, sent_bitrate);
+ EXPECT_LE(start_rate, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(available_channel_estimate, sent_bitrate);
+
+ // First convex growth, from available_channel_estimate.
+ for (int j = 0; j < 23; ++j) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(available_channel_estimate, sent_bitrate);
+ EXPECT_GE(halfway_point, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(halfway_point, sent_bitrate);
+
+ // Second concave growth, towards channel_estimate.
+ for (int i = 0; i < 12; ++i) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_GE(channel_estimate, sent_bitrate);
+ EXPECT_LE(halfway_point, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(channel_estimate, sent_bitrate);
+
+ // Second convex growth, from channel_estimate.
+ for (int j = 0; j < 30; ++j) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(channel_estimate, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 100000,
+ sent_bitrate.ToBytesPerSecond(), 10000);
+
+ // Verify that we increase cubic.
+ for (int j = 0; j < 23; ++j) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(channel_estimate, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 750000,
+ sent_bitrate.ToBytesPerSecond(), 20000);
+}
+
+TEST_F(InterArrivalBitrateRampUpTest, UnknownEstimates) {
+ QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100);
+ QuicBandwidth available_channel_estimate =
+ QuicBandwidth::FromKBytesPerSecond(200);
+ QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(400);
+ QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
+ bitrate_ramp_up_.Reset(start_rate,
+ available_channel_estimate,
+ available_channel_estimate);
+
+ // First convex growth, from start_rate.
+ for (int j = 0; j < 20; ++j) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(start_rate, sent_bitrate);
+ EXPECT_GE(available_channel_estimate, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_NEAR(available_channel_estimate.ToBytesPerSecond(),
+ sent_bitrate.ToBytesPerSecond(), 10000);
+
+ // Verify that we increase cubic.
+ for (int j = 0; j < 31; ++j) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_GE(channel_estimate, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_NEAR(channel_estimate.ToBytesPerSecond(),
+ sent_bitrate.ToBytesPerSecond(), 10000);
+}
+
+TEST_F(InterArrivalBitrateRampUpTest, UpdatingChannelEstimateHigher) {
+ QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(200);
+ QuicBandwidth available_channel_estimate = start_rate;
+ QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(250);
+ QuicBandwidth halfway_point = available_channel_estimate.Add(
+ channel_estimate.Subtract(available_channel_estimate).Scale(0.5f));
+ QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
+ bitrate_ramp_up_.Reset(start_rate,
+ available_channel_estimate,
+ channel_estimate);
+
+ // Convex growth, from available_channel_estimate.
+ for (int j = 0; j < 16; ++j) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(available_channel_estimate, sent_bitrate);
+ EXPECT_GE(halfway_point, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(halfway_point, sent_bitrate);
+
+ // Increse channel estimate.
+ channel_estimate = QuicBandwidth::FromKBytesPerSecond(300);
+ bitrate_ramp_up_.UpdateChannelEstimate(channel_estimate);
+
+ // Concave growth, towards channel_estimate.
+ for (int i = 0; i < 22; ++i) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_GE(channel_estimate, sent_bitrate);
+ EXPECT_LE(halfway_point, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(channel_estimate, sent_bitrate);
+}
+
+TEST_F(InterArrivalBitrateRampUpTest, UpdatingChannelEstimateLower) {
+ QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(200);
+ QuicBandwidth available_channel_estimate = start_rate;
+ QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(250);
+ QuicBandwidth halfway_point = available_channel_estimate.Add(
+ channel_estimate.Subtract(available_channel_estimate).Scale(0.5f));
+ QuicBandwidth sent_bitrate = QuicBandwidth::Zero();
+ bitrate_ramp_up_.Reset(start_rate,
+ available_channel_estimate,
+ channel_estimate);
+
+ // Convex growth, from available_channel_estimate.
+ for (int j = 0; j < 16; ++j) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(available_channel_estimate, sent_bitrate);
+ EXPECT_GE(halfway_point, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(halfway_point, sent_bitrate);
+
+ // Decrese channel estimate.
+ channel_estimate = QuicBandwidth::FromKBytesPerSecond(240);
+ bitrate_ramp_up_.UpdateChannelEstimate(channel_estimate);
+
+ // Concave growth, towards channel_estimate.
+ for (int i = 0; i < 11; ++i) {
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_GE(channel_estimate, sent_bitrate);
+ EXPECT_LE(halfway_point, sent_bitrate);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate);
+ EXPECT_LE(channel_estimate, sent_bitrate);
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/congestion_control/inter_arrival_overuse_detector.cc b/net/quic/congestion_control/inter_arrival_overuse_detector.cc
new file mode 100644
index 0000000..73e005d
--- /dev/null
+++ b/net/quic/congestion_control/inter_arrival_overuse_detector.cc
@@ -0,0 +1,258 @@
+// 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 "net/quic/congestion_control/inter_arrival_overuse_detector.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+// Initial noise variance, equal to a standard deviation of 1 millisecond.
+static const float kInitialVarianceNoise = 1000000.0;
+
+// Minimum variance of the time delta.
+static const int kMinVarianceDelta = 10000;
+
+// Threshold for accumulated delta.
+static const int kThresholdAccumulatedDeltasUs = 1000;
+
+// The higher the beta parameter, the lower is the effect of the input and the
+// more damping of the noise. And the longer time for a detection.
+static const float kBeta = 0.98f;
+
+// Same as above, described as numerator and denominator.
+static const int kBetaNumerator = 49;
+static const int kBetaDenominator = 50;
+
+// Trigger a signal when the accumulated time drift is larger than
+// 5 x standard deviation.
+// A lower value triggers earlier with more false detect as a side effect.
+static const int kDetectDriftStandardDeviation = 5;
+
+// Trigger an overuse when the time difference between send time and receive
+// is larger than 7 x standard deviation.
+// A lower value triggers earlier with more false detect as a side effect.
+static const float kDetectTimeDiffStandardDeviation = 7;
+
+// Trigger an overuse when the mean of the time difference diverges too far
+// from 0.
+// A higher value trigger earlier with more false detect as a side effect.
+static const int kDetectSlopeFactor = 14;
+
+// We need to get some initial statistics before the detection can start.
+static const int kMinSamplesBeforeDetect = 10;
+
+namespace net {
+
+InterArrivalOveruseDetector::InterArrivalOveruseDetector()
+ : last_sequence_number_(0),
+ num_of_deltas_(0),
+ accumulated_deltas_(QuicTime::Delta::Zero()),
+ delta_mean_(0.0),
+ delta_variance_(kInitialVarianceNoise),
+ delta_overuse_counter_(0),
+ delta_estimate_(kBandwidthSteady),
+ slope_overuse_counter_(0),
+ slope_estimate_(kBandwidthSteady),
+ send_receive_offset_(QuicTime::Delta::Infinite()),
+ estimated_congestion_delay_(QuicTime::Delta::Zero()) {
+}
+
+void InterArrivalOveruseDetector::OnAcknowledgedPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicTime send_time,
+ bool last_of_send_time,
+ QuicTime receive_time) {
+ if (last_sequence_number_ >= sequence_number) {
+ // This is an old packet and should be ignored. Note that we are called
+ // with a full 64 bit sequence number, even if the wire format may only
+ // convey some low-order bits of that number.
+ DLOG(INFO) << "Skip old packet";
+ return;
+ }
+
+ last_sequence_number_ = sequence_number;
+
+ if (current_packet_group_.send_time != send_time) {
+ // First value in this group. If the last packet of a group is lost we
+ // overwrite the old value and start over with a new measurement.
+ current_packet_group_.send_time = send_time;
+ // The receive_time value might not be first in a packet burst if that
+ // packet was lost, however we will still use it in this calculation.
+ UpdateSendReceiveTimeOffset(receive_time.Subtract(send_time));
+ }
+ if (!last_of_send_time) {
+ // We expect more packet with this send time.
+ return;
+ }
+ // First packet of a later group, the previous group sample is ready.
+ if (previous_packet_group_.send_time.IsInitialized()) {
+ QuicTime::Delta sent_delta = send_time.Subtract(
+ previous_packet_group_.send_time);
+ QuicTime::Delta receive_delta = receive_time.Subtract(
+ previous_packet_group_.last_receive_time);
+ // We assume that groups of packets are sent together as bursts (tagged
+ // with identical send times) because it is too computationally expensive
+ // to pace them out individually. The received_delta is then the total
+ // delta between the receipt of the last packet of the previous group, and
+ // the last packet of the current group. Assuming we are transmitting
+ // these bursts on average at the available bandwidth rate, there should be
+ // no change in overall spread (both deltas should be the same).
+ UpdateFilter(receive_delta, sent_delta);
+ }
+ // Save current as previous.
+ previous_packet_group_ = current_packet_group_;
+ previous_packet_group_.last_receive_time = receive_time;
+}
+
+void InterArrivalOveruseDetector::UpdateSendReceiveTimeOffset(
+ QuicTime::Delta offset) {
+ // Note the send and receive time can have a randomly large offset, however
+ // they are stable in relation to each other, hence no or extremely low clock
+ // drift relative to the duration of our stream.
+ if (offset.ToMicroseconds() < send_receive_offset_.ToMicroseconds()) {
+ send_receive_offset_ = offset;
+ }
+ estimated_congestion_delay_ = offset.Subtract(send_receive_offset_);
+}
+
+BandwidthUsage InterArrivalOveruseDetector::GetState(
+ QuicTime::Delta* estimated_congestion_delay) {
+ *estimated_congestion_delay = estimated_congestion_delay_;
+ int64 sigma_delta = sqrt(static_cast<double>(delta_variance_));
+ DetectSlope(sigma_delta);
+ DetectDrift(sigma_delta);
+ return std::max(slope_estimate_, delta_estimate_);
+}
+
+void InterArrivalOveruseDetector::UpdateFilter(QuicTime::Delta received_delta,
+ QuicTime::Delta sent_delta) {
+ ++num_of_deltas_;
+ QuicTime::Delta time_diff = received_delta.Subtract(sent_delta);
+ UpdateDeltaEstimate(time_diff);
+ accumulated_deltas_ = accumulated_deltas_.Add(time_diff);
+}
+
+void InterArrivalOveruseDetector::UpdateDeltaEstimate(
+ QuicTime::Delta residual) {
+ DCHECK_EQ(1, kBetaDenominator - kBetaNumerator);
+ int64 residual_us = residual.ToMicroseconds();
+ delta_mean_ =
+ (kBetaNumerator * delta_mean_ + residual_us) / kBetaDenominator;
+ delta_variance_ =
+ (kBetaNumerator * delta_variance_ +
+ (delta_mean_ - residual_us) * (delta_mean_ - residual_us)) /
+ kBetaDenominator;
+
+ if (delta_variance_ < kMinVarianceDelta) {
+ delta_variance_ = kMinVarianceDelta;
+ }
+}
+
+void InterArrivalOveruseDetector::DetectDrift(int64 sigma_delta) {
+ // We have 2 drift detectors. The accumulate of deltas and the absolute time
+ // differences.
+ if (num_of_deltas_ < kMinSamplesBeforeDetect) {
+ return;
+ }
+ if (delta_overuse_counter_ > 0 &&
+ accumulated_deltas_.ToMicroseconds() > kThresholdAccumulatedDeltasUs) {
+ if (delta_estimate_ != kBandwidthDraining) {
+ DLOG(INFO) << "Bandwidth estimate drift: Draining buffer(s) "
+ << accumulated_deltas_.ToMilliseconds() << " ms";
+ delta_estimate_ = kBandwidthDraining;
+ }
+ return;
+ }
+ if ((sigma_delta * kDetectTimeDiffStandardDeviation >
+ estimated_congestion_delay_.ToMicroseconds()) &&
+ (sigma_delta * kDetectDriftStandardDeviation >
+ abs(accumulated_deltas_.ToMicroseconds()))) {
+ if (delta_estimate_ != kBandwidthSteady) {
+ DLOG(INFO) << "Bandwidth estimate drift: Steady"
+ << " mean:" << delta_mean_
+ << " sigma:" << sigma_delta
+ << " offset:" << send_receive_offset_.ToMicroseconds()
+ << " delta:" << estimated_congestion_delay_.ToMicroseconds()
+ << " drift:" << accumulated_deltas_.ToMicroseconds();
+ delta_estimate_ = kBandwidthSteady;
+ // Reset drift counter.
+ accumulated_deltas_ = QuicTime::Delta::Zero();
+ delta_overuse_counter_ = 0;
+ }
+ return;
+ }
+ if (accumulated_deltas_.ToMicroseconds() > 0) {
+ if (delta_estimate_ != kBandwidthOverUsing) {
+ ++delta_overuse_counter_;
+ DLOG(INFO) << "Bandwidth estimate drift: Over using"
+ << " mean:" << delta_mean_
+ << " sigma:" << sigma_delta
+ << " offset:" << send_receive_offset_.ToMicroseconds()
+ << " delta:" << estimated_congestion_delay_.ToMicroseconds()
+ << " drift:" << accumulated_deltas_.ToMicroseconds();
+ delta_estimate_ = kBandwidthOverUsing;
+ }
+ } else {
+ if (delta_estimate_ != kBandwidthUnderUsing) {
+ --delta_overuse_counter_;
+ DLOG(INFO) << "Bandwidth estimate drift: Under using"
+ << " mean:" << delta_mean_
+ << " sigma:" << sigma_delta
+ << " offset:" << send_receive_offset_.ToMicroseconds()
+ << " delta:" << estimated_congestion_delay_.ToMicroseconds()
+ << " drift:" << accumulated_deltas_.ToMicroseconds();
+ delta_estimate_ = kBandwidthUnderUsing;
+ }
+ // Adding decay of negative accumulated_deltas_ since it could be caused by
+ // a starting with full buffers. This way we will always converge to 0.
+ accumulated_deltas_ = accumulated_deltas_.Add(
+ QuicTime::Delta::FromMicroseconds(sigma_delta >> 3));
+ }
+}
+
+void InterArrivalOveruseDetector::DetectSlope(int64 sigma_delta) {
+ // We use the mean change since it has a constant expected mean 0
+ // regardless of number of packets and spread. It is also safe to use during
+ // packet loss, since a lost packet only results in a missed filter update
+ // not a drift.
+ if (num_of_deltas_ < kMinSamplesBeforeDetect) {
+ return;
+ }
+ if (slope_overuse_counter_ > 0 && delta_mean_ > 0) {
+ if (slope_estimate_ != kBandwidthDraining) {
+ DLOG(INFO) << "Bandwidth estimate slope: Draining buffer(s)";
+ }
+ slope_estimate_ = kBandwidthDraining;
+ return;
+ }
+ if (sigma_delta > abs(delta_mean_) * kDetectSlopeFactor) {
+ if (slope_estimate_ != kBandwidthSteady) {
+ DLOG(INFO) << "Bandwidth estimate slope: Steady"
+ << " mean:" << delta_mean_
+ << " sigma:" << sigma_delta;
+ slope_overuse_counter_ = 0;
+ slope_estimate_ = kBandwidthSteady;
+ }
+ return;
+ }
+ if (delta_mean_ > 0) {
+ if (slope_estimate_ != kBandwidthOverUsing) {
+ ++slope_overuse_counter_;
+ DLOG(INFO) << "Bandwidth estimate slope: Over using"
+ << " mean:" << delta_mean_
+ << " sigma:" << sigma_delta;
+ slope_estimate_ = kBandwidthOverUsing;
+ }
+ } else {
+ if (slope_estimate_ != kBandwidthUnderUsing) {
+ --slope_overuse_counter_;
+ DLOG(INFO) << "Bandwidth estimate slope: Under using"
+ << " mean:" << delta_mean_
+ << " sigma:" << sigma_delta;
+ slope_estimate_ = kBandwidthUnderUsing;
+ }
+ }
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/inter_arrival_overuse_detector.h b/net/quic/congestion_control/inter_arrival_overuse_detector.h
new file mode 100644
index 0000000..1852362
--- /dev/null
+++ b/net/quic/congestion_control/inter_arrival_overuse_detector.h
@@ -0,0 +1,173 @@
+// 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.
+
+// This class is a helper class to the inter arrival congestion control. It
+// provide a signal to the inter arrival congestion control of the estimated
+// state of our transport channel. The estimate is based on the inter arrival
+// time of the received packets relative to the time those packets were sent;
+// we can estimate the build up of buffers on the network before packets are
+// lost.
+//
+// Note: this class is not thread-safe.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_OVERUSE_DETECTOR_H_
+#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_OVERUSE_DETECTOR_H_
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+enum NET_EXPORT_PRIVATE RateControlRegion {
+ kRateControlRegionUnknown = 0,
+ kRateControlRegionUnderMax = 1,
+ kRateControlRegionNearMax = 2
+};
+
+// Note: Order is important.
+enum NET_EXPORT_PRIVATE BandwidthUsage {
+ kBandwidthSteady = 0,
+ kBandwidthUnderUsing = 1,
+ kBandwidthDraining = 2,
+ kBandwidthOverUsing = 3,
+};
+
+// Normal state transition diagram
+//
+// kBandwidthUnderUsing
+// |
+// |
+// kBandwidthSteady
+// | ^
+// | |
+// kBandwidthOverUsing |
+// | |
+// | |
+// kBandwidthDraining
+//
+// The above transitions is in normal operation, with extreme values we don't
+// enforce the state transitions, hence you could in extreme scenarios go
+// between any states.
+//
+// kBandwidthSteady When the packets arrive in the same pace as we sent
+// them. In this state we can increase our send pace.
+//
+// kBandwidthOverUsing When the packets arrive slower than the pace we sent
+// them. In this state we should decrease our send pace.
+// When we enter into this state we will also get an
+// estimate on how much delay we have built up. The
+// reduction in send pace should be chosen to drain the
+// built up delay within reasonable time.
+//
+// kBandwidthUnderUsing When the packets arrive faster than the pace we sent
+// them. In this state another stream disappeared from
+// a shared link leaving us more available bandwidth.
+// In this state we should hold our pace to make sure we
+// fully drain the buffers before we start increasing
+// our send rate. We do this to avoid operating with
+// semi-full buffers.
+//
+// kBandwidthDraining We can only be in this state after we have been in a
+// overuse state. In this state we should hold our pace
+// to make sure we fully drain the buffers before we
+// start increasing our send rate. We do this to avoid
+// operating with semi-full buffers.
+
+class NET_EXPORT_PRIVATE InterArrivalOveruseDetector {
+ public:
+ InterArrivalOveruseDetector();
+
+ // Update the statistics with the received delta times, call for every
+ // received delta time. This function assumes that there is no re-orderings.
+ // If multiple packets are sent at the same time (identical send_time)
+ // last_of_send_time should be set to false for all but the last calls to
+ // this function. If there is only one packet sent at a given time
+ // last_of_send_time must be true.
+ // received_delta is the time difference between receiving this packet and the
+ // previously received packet.
+ void OnAcknowledgedPacket(QuicPacketSequenceNumber sequence_number,
+ QuicTime send_time,
+ bool last_of_send_time,
+ QuicTime receive_time);
+
+ // Get the current estimated state and update the estimated congestion delay.
+ // |estimated_congestion_delay| will be updated with the estimated built up
+ // buffer delay; it must not be NULL as it will be updated with the estimate.
+ // Note 1: estimated_buffer_delay will only be valid when kBandwidthOverUsing
+ // is returned.
+ // Note 2: it's assumed that the pacer lower its send pace to drain the
+ // built up buffer within reasonable time. The pacer should use the
+ // estimated_buffer_delay as a guidance on how much to back off.
+ // Note 3: The absolute value of estimated_congestion_delay is less reliable
+ // than the state itself. It is also biased to low since we can't know
+ // how full the buffers are when the flow starts.
+ BandwidthUsage GetState(QuicTime::Delta* estimated_congestion_delay);
+
+ private:
+ struct PacketGroup {
+ PacketGroup()
+ : send_time(QuicTime::Zero()),
+ last_receive_time(QuicTime::Zero()) {
+ }
+ QuicTime send_time;
+ QuicTime last_receive_time;
+ };
+
+ // Update the statistics with the absolute receive time relative to the
+ // absolute send time.
+ void UpdateSendReceiveTimeOffset(QuicTime::Delta offset);
+
+ // Update the filter with this new data point.
+ void UpdateFilter(QuicTime::Delta received_delta,
+ QuicTime::Delta sent_delta);
+
+ // Update the estimate with this residual.
+ void UpdateDeltaEstimate(QuicTime::Delta residual);
+
+ // Estimate the state based on the slope of the changes.
+ void DetectSlope(int64 sigma_delta);
+
+ // Estimate the state based on the accumulated drift of the changes.
+ void DetectDrift(int64 sigma_delta);
+
+ // Current grouping of packets that were sent at the same time.
+ PacketGroup current_packet_group_;
+ // Grouping of packets that were sent at the same time, just before the
+ // current_packet_group_ above.
+ PacketGroup previous_packet_group_;
+ // Sequence number of the last acknowledged packet.
+ QuicPacketSequenceNumber last_sequence_number_;
+ // Number of received delta times with unique send time.
+ int num_of_deltas_;
+ // Estimated accumulation of received delta times.
+ // Note: Can be negative and can drift over time which is why we bias it
+ // towards 0 and reset it given some triggers.
+ QuicTime::Delta accumulated_deltas_;
+ // Current running mean of our received delta times.
+ int delta_mean_;
+ // Current running variance of our received delta times.
+ int64 delta_variance_;
+ // Number of overuse signals currently triggered in this state.
+ // Note: negative represent underuse.
+ int delta_overuse_counter_;
+ // State estimated by the delta times.
+ BandwidthUsage delta_estimate_;
+ // Number of overuse signals currently triggered in this state.
+ // Note: negative represent underuse.
+ int slope_overuse_counter_;
+ // State estimated by the slope of the delta times.
+ BandwidthUsage slope_estimate_;
+ // Lowest offset between send and receive time ever received in this session.
+ QuicTime::Delta send_receive_offset_;
+ // Last received time difference between our normalized send and receive time.
+ QuicTime::Delta estimated_congestion_delay_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterArrivalOveruseDetector);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_OVERUSE_DETECTOR_H_
diff --git a/net/quic/congestion_control/inter_arrival_overuse_detector_test.cc b/net/quic/congestion_control/inter_arrival_overuse_detector_test.cc
new file mode 100644
index 0000000..8d37749
--- /dev/null
+++ b/net/quic/congestion_control/inter_arrival_overuse_detector_test.cc
@@ -0,0 +1,1114 @@
+// 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 <stdlib.h>
+
+#include <cmath>
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "net/quic/congestion_control/inter_arrival_overuse_detector.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+static const double kPi = 3.14159265;
+
+namespace net {
+namespace test {
+
+class InterArrivalOveruseDetectorTest : public ::testing::Test {
+ protected:
+ InterArrivalOveruseDetectorTest();
+
+ QuicTime::Delta GaussianRandom(QuicTime::Delta mean,
+ QuicTime::Delta standard_deviation);
+
+ int Run100000Samples(int packets_per_burst,
+ QuicTime::Delta mean,
+ QuicTime::Delta standard_deviation);
+
+ int RunUntilOveruse(int packets_per_burst,
+ QuicTime::Delta mean,
+ QuicTime::Delta standard_deviation,
+ QuicTime::Delta drift_per_burst,
+ QuicTime::Delta *estimated_buffer_delay);
+
+ int RunUntilSteady(int packets_per_burst,
+ QuicTime::Delta mean,
+ QuicTime::Delta standard_deviation,
+ QuicTime::Delta drift_per_burst);
+
+ int RunUntilNotDraining(int packets_per_burst,
+ QuicTime::Delta mean,
+ QuicTime::Delta standard_deviation,
+ QuicTime::Delta drift_per_burst);
+
+ int RunUntilUnderusing(int packets_per_burst,
+ QuicTime::Delta mean,
+ QuicTime::Delta standard_deviation,
+ QuicTime::Delta drift_per_burst);
+
+ void RunXBursts(int bursts,
+ int packets_per_burst,
+ QuicTime::Delta mean,
+ QuicTime::Delta standard_deviation,
+ QuicTime::Delta drift_per_burst);
+
+ QuicPacketSequenceNumber sequence_number_;
+ MockClock send_clock_;
+ MockClock receive_clock_;
+ QuicTime::Delta drift_from_mean_;
+ InterArrivalOveruseDetector overuse_detector_;
+ unsigned int seed_;
+};
+
+InterArrivalOveruseDetectorTest::InterArrivalOveruseDetectorTest()
+ : sequence_number_(1),
+ drift_from_mean_(QuicTime::Delta::Zero()),
+ seed_(1234) {
+}
+
+QuicTime::Delta InterArrivalOveruseDetectorTest::GaussianRandom(
+ QuicTime::Delta mean,
+ QuicTime::Delta standard_deviation) {
+ // Creating a Normal distribution variable from two independent uniform
+ // variables based on the Box-Muller transform.
+ double uniform1 = base::RandDouble();
+ double uniform2 = base::RandDouble();
+
+ QuicTime::Delta random = QuicTime::Delta::FromMicroseconds(
+ static_cast<int>(standard_deviation.ToMicroseconds() *
+ sqrt(-2 * log(uniform1)) * cos(2 * kPi * uniform2))).
+ Add(mean).Subtract(drift_from_mean_);
+ if (random < QuicTime::Delta::Zero()) {
+ // Don't do negative deltas.
+ drift_from_mean_ = drift_from_mean_.Subtract(mean);
+ return QuicTime::Delta::Zero();
+ }
+ drift_from_mean_ = drift_from_mean_.Add(random).Subtract(mean);
+ return random;
+}
+
+int InterArrivalOveruseDetectorTest::Run100000Samples(
+ int packets_per_burst,
+ QuicTime::Delta mean,
+ QuicTime::Delta standard_deviation) {
+ int unique_overuse = 0;
+ int last_overuse = -1;
+ for (int i = 0; i < 100000; ++i) {
+ // Assume that we send out the packets with perfect pacing.
+ send_clock_.AdvanceTime(mean);
+ QuicTime send_time = send_clock_.ApproximateNow();
+ // Do only one random delta for all packets in a burst.
+ receive_clock_.AdvanceTime(GaussianRandom(mean, standard_deviation));
+ QuicTime receive_time = receive_clock_.ApproximateNow();
+ for (int j = 0; j <= packets_per_burst; ++j) {
+ overuse_detector_.OnAcknowledgedPacket(sequence_number_++,
+ send_time,
+ (j == packets_per_burst),
+ receive_time);
+ }
+ // We expect to randomly hit a few false detects, count the unique
+ // overuse events, hence not multiple signals in a row.
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ if (kBandwidthOverUsing == overuse_detector_.GetState(
+ &estimated_buffer_delay)) {
+ if (last_overuse + 1 != i) {
+ unique_overuse++;
+ }
+ last_overuse = i;
+ }
+ }
+ return unique_overuse;
+}
+
+int InterArrivalOveruseDetectorTest::RunUntilOveruse(
+ int packets_per_burst,
+ QuicTime::Delta mean,
+ QuicTime::Delta standard_deviation,
+ QuicTime::Delta drift_per_burst,
+ QuicTime::Delta *estimated_buffer_delay) {
+ // Simulate a higher send pace, that is too high.
+ for (int i = 0; i < 1000; ++i) {
+ send_clock_.AdvanceTime(mean);
+ QuicTime send_time = send_clock_.ApproximateNow();
+ // Do only one random delta for all packets in a burst.
+ receive_clock_.AdvanceTime(GaussianRandom(mean.Add(drift_per_burst),
+ standard_deviation));
+ QuicTime receive_time = receive_clock_.ApproximateNow();
+ for (int j = 0; j <= packets_per_burst; ++j) {
+ overuse_detector_.OnAcknowledgedPacket(sequence_number_++,
+ send_time,
+ (j == packets_per_burst),
+ receive_time);
+ }
+ if (kBandwidthOverUsing == overuse_detector_.GetState(
+ estimated_buffer_delay)) {
+ return i + 1;
+ }
+ }
+ return -1;
+}
+
+int InterArrivalOveruseDetectorTest::RunUntilSteady(
+ int packets_per_burst,
+ QuicTime::Delta mean,
+ QuicTime::Delta standard_deviation,
+ QuicTime::Delta drift_per_burst) {
+ // Simulate a lower send pace, that is lower than the capacity.
+ for (int i = 0; i < 1000; ++i) {
+ send_clock_.AdvanceTime(mean);
+ QuicTime send_time = send_clock_.ApproximateNow();
+ // Do only one random delta for all packets in a burst.
+ receive_clock_.AdvanceTime(GaussianRandom(mean.Subtract(drift_per_burst),
+ standard_deviation));
+ QuicTime receive_time = receive_clock_.ApproximateNow();
+ for (int j = 0; j <= packets_per_burst; ++j) {
+ overuse_detector_.OnAcknowledgedPacket(sequence_number_++,
+ send_time,
+ (j == packets_per_burst),
+ receive_time);
+ }
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ if (kBandwidthSteady ==
+ overuse_detector_.GetState(&estimated_buffer_delay)) {
+ return i + 1;
+ }
+ }
+ return -1;
+}
+
+int InterArrivalOveruseDetectorTest::RunUntilNotDraining(
+ int packets_per_burst,
+ QuicTime::Delta mean,
+ QuicTime::Delta standard_deviation,
+ QuicTime::Delta drift_per_burst) {
+ // Simulate a lower send pace, that is lower than the capacity.
+ for (int i = 0; i < 1000; ++i) {
+ send_clock_.AdvanceTime(mean);
+ QuicTime send_time = send_clock_.ApproximateNow();
+ // Do only one random delta for all packets in a burst.
+ receive_clock_.AdvanceTime(GaussianRandom(mean.Subtract(drift_per_burst),
+ standard_deviation));
+ QuicTime receive_time = receive_clock_.ApproximateNow();
+ for (int j = 0; j <= packets_per_burst; ++j) {
+ overuse_detector_.OnAcknowledgedPacket(sequence_number_++,
+ send_time,
+ (j == packets_per_burst),
+ receive_time);
+ }
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ if (kBandwidthDraining >
+ overuse_detector_.GetState(&estimated_buffer_delay)) {
+ return i + 1;
+ }
+ }
+ return -1;
+}
+
+int InterArrivalOveruseDetectorTest::RunUntilUnderusing(
+ int packets_per_burst,
+ QuicTime::Delta mean,
+ QuicTime::Delta standard_deviation,
+ QuicTime::Delta drift_per_burst) {
+ // Simulate a lower send pace, that is lower than the capacity.
+ for (int i = 0; i < 1000; ++i) {
+ send_clock_.AdvanceTime(mean);
+ QuicTime send_time = send_clock_.ApproximateNow();
+ // Do only one random delta for all packets in a burst.
+ receive_clock_.AdvanceTime(GaussianRandom(mean.Subtract(drift_per_burst),
+ standard_deviation));
+ QuicTime receive_time = receive_clock_.ApproximateNow();
+ for (int j = 0; j <= packets_per_burst; ++j) {
+ overuse_detector_.OnAcknowledgedPacket(sequence_number_++,
+ send_time,
+ (j == packets_per_burst),
+ receive_time);
+ }
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ if (kBandwidthUnderUsing == overuse_detector_.GetState(
+ &estimated_buffer_delay)) {
+ return i + 1;
+ }
+ }
+ return -1;
+}
+
+void InterArrivalOveruseDetectorTest::RunXBursts(
+ int bursts,
+ int packets_per_burst,
+ QuicTime::Delta mean,
+ QuicTime::Delta standard_deviation,
+ QuicTime::Delta drift_per_burst) {
+ for (int i = 0; i < bursts; ++i) {
+ send_clock_.AdvanceTime(mean);
+ QuicTime send_time = send_clock_.ApproximateNow();
+ // Do only one random delta for all packets in a burst.
+ receive_clock_.AdvanceTime(GaussianRandom(mean.Add(drift_per_burst),
+ standard_deviation));
+ QuicTime receive_time = receive_clock_.ApproximateNow();
+ for (int j = 0; j <= packets_per_burst; ++j) {
+ overuse_detector_.OnAcknowledgedPacket(sequence_number_++,
+ send_time,
+ (j == packets_per_burst),
+ receive_time);
+ }
+ }
+}
+
+// TODO(pwestin): test packet loss impact on accuracy.
+// TODO(pwestin): test colored noise by dropping late frames.
+
+TEST_F(InterArrivalOveruseDetectorTest, DISABLED_TestNoise) {
+ int count[100];
+ memset(count, 0, sizeof(count));
+ for (int i = 0; i < 10000; ++i) {
+ QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(30);
+ QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(10);
+ count[GaussianRandom(mean, standard_deviation).ToMilliseconds()]++;
+ }
+ for (int j = 0; j < 100; ++j) {
+ DLOG(INFO) << j << ":" << count[j];
+ }
+}
+
+TEST_F(InterArrivalOveruseDetectorTest, DISABLED_SimpleNonOveruse) {
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(10);
+
+ for (int i = 0; i < 1000; ++i) {
+ QuicTime send_time = send_clock_.ApproximateNow();
+ QuicTime receive_time = receive_clock_.ApproximateNow();
+ overuse_detector_.OnAcknowledgedPacket(sequence_number++,
+ send_time,
+ true,
+ receive_time);
+ send_clock_.AdvanceTime(delta);
+ receive_clock_.AdvanceTime(delta);
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ EXPECT_EQ(kBandwidthSteady,
+ overuse_detector_.GetState(&estimated_buffer_delay));
+ }
+}
+
+TEST_F(InterArrivalOveruseDetectorTest,
+ DISABLED_SimpleNonOveruseSendClockAhead) {
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(10);
+ send_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1234));
+
+ for (int i = 0; i < 1000; ++i) {
+ QuicTime send_time = send_clock_.ApproximateNow();
+ QuicTime receive_time = receive_clock_.ApproximateNow();
+ overuse_detector_.OnAcknowledgedPacket(sequence_number++,
+ send_time,
+ true,
+ receive_time);
+ send_clock_.AdvanceTime(delta);
+ receive_clock_.AdvanceTime(delta);
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ EXPECT_EQ(kBandwidthSteady,
+ overuse_detector_.GetState(&estimated_buffer_delay));
+ }
+}
+
+TEST_F(InterArrivalOveruseDetectorTest,
+ DISABLED_SimpleNonOveruseSendClockBehind) {
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(10);
+ receive_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1234));
+
+ for (int i = 0; i < 1000; ++i) {
+ QuicTime send_time = send_clock_.ApproximateNow();
+ QuicTime receive_time = receive_clock_.ApproximateNow();
+ overuse_detector_.OnAcknowledgedPacket(sequence_number++,
+ send_time,
+ true,
+ receive_time);
+ send_clock_.AdvanceTime(delta);
+ receive_clock_.AdvanceTime(delta);
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ EXPECT_EQ(kBandwidthSteady,
+ overuse_detector_.GetState(&estimated_buffer_delay));
+ }
+}
+
+TEST_F(InterArrivalOveruseDetectorTest, DISABLED_SimpleNonOveruseWithVariance) {
+ QuicPacketSequenceNumber sequence_number = 1;
+ for (int i = 0; i < 1000; ++i) {
+ if (i % 2) {
+ receive_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ } else {
+ receive_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(15));
+ }
+ QuicTime send_time = send_clock_.ApproximateNow();
+ QuicTime receive_time = receive_clock_.ApproximateNow();
+ overuse_detector_.OnAcknowledgedPacket(sequence_number++,
+ send_time,
+ true,
+ receive_time);
+ send_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ EXPECT_EQ(kBandwidthSteady,
+ overuse_detector_.GetState(&estimated_buffer_delay));
+ }
+}
+
+TEST_F(InterArrivalOveruseDetectorTest, DISABLED_SimpleOveruse) {
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10);
+ QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(5);
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+
+ for (int i = 0; i < 100000; ++i) {
+ send_clock_.AdvanceTime(send_delta);
+ receive_clock_.AdvanceTime(received_delta);
+ QuicTime send_time = send_clock_.ApproximateNow();
+ QuicTime receive_time = receive_clock_.ApproximateNow();
+ // Sending 2 packets the same time as that is what we expect to do.
+ overuse_detector_.OnAcknowledgedPacket(sequence_number++,
+ send_time,
+ false,
+ receive_time);
+ receive_clock_.AdvanceTime(received_delta);
+ receive_time = receive_clock_.ApproximateNow();
+ overuse_detector_.OnAcknowledgedPacket(sequence_number++,
+ send_time,
+ true,
+ receive_time);
+
+ EXPECT_EQ(kBandwidthSteady,
+ overuse_detector_.GetState(&estimated_buffer_delay));
+ }
+ // Simulate a higher send pace, that is too high by receiving 1 millisecond
+ // late per packet.
+ received_delta = QuicTime::Delta::FromMilliseconds(6);
+ send_clock_.AdvanceTime(send_delta);
+ receive_clock_.AdvanceTime(received_delta);
+ QuicTime send_time = send_clock_.ApproximateNow();
+ QuicTime receive_time = receive_clock_.ApproximateNow();
+ overuse_detector_.OnAcknowledgedPacket(sequence_number++,
+ send_time,
+ false,
+ receive_time);
+ receive_clock_.AdvanceTime(received_delta);
+ receive_time = receive_clock_.ApproximateNow();
+ overuse_detector_.OnAcknowledgedPacket(sequence_number++,
+ send_time,
+ true,
+ receive_time);
+ EXPECT_EQ(kBandwidthOverUsing,
+ overuse_detector_.GetState(&estimated_buffer_delay));
+}
+
+TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance10Kbit) {
+ int packets_per_burst = 1;
+ QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1000);
+ QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5);
+ QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(5);
+
+ int overuse_signals = Run100000Samples(packets_per_burst,
+ mean,
+ standard_deviation);
+ EXPECT_GE(1, overuse_signals);
+
+ // Simulate a higher send pace, that is too high.
+ // With current tuning we require 6 updates with 5 milliseconds before
+ // detection.
+ // Resulting in a minimal buffer build up of 30 milliseconds.
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst,
+ &estimated_buffer_delay);
+ EXPECT_GE(6, bursts_until_overuse);
+ EXPECT_NEAR(40, estimated_buffer_delay.ToMilliseconds(), 15);
+
+ // After draining the buffers we are back in a normal state.
+ int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(6, bursts_until_not_draining);
+
+ // After draining the buffer additionally we detect an underuse.
+ int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(7, bursts_until_underusing);
+}
+
+TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance10Kbit) {
+ int packets_per_burst = 1;
+ QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1000);
+ QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(50);
+ QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(50);
+
+ int overuse_signals = Run100000Samples(packets_per_burst,
+ mean,
+ standard_deviation);
+ EXPECT_GE(1, overuse_signals);
+
+ // Simulate a higher send pace, that is too high.
+ // With current tuning we require 6 updates with 50 milliseconds before
+ // detection.
+ // Resulting in a minimal buffer build up of 300 milliseconds.
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst,
+ &estimated_buffer_delay);
+ EXPECT_GE(6, bursts_until_overuse);
+ EXPECT_NEAR(400, estimated_buffer_delay.ToMilliseconds(), 150);
+
+ // After draining the buffers we are back in a normal state.
+ int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(6, bursts_until_not_draining);
+
+ // After draining the buffer additionally we detect an underuse.
+ int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(7, bursts_until_underusing);
+}
+
+TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance100Kbit) {
+ int packets_per_burst = 1;
+ QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(100);
+ QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5);
+ QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(5);
+
+ int overuse_signals = Run100000Samples(packets_per_burst,
+ mean,
+ standard_deviation);
+ EXPECT_GE(1, overuse_signals);
+
+ // Simulate a higher send pace, that is too high.
+ // With current tuning we require 6 updates with 5 milliseconds
+ // before detection.
+ // Resulting in a minimal buffer build up of 30 ms.
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst,
+ &estimated_buffer_delay);
+ EXPECT_GE(6, bursts_until_overuse);
+ EXPECT_NEAR(40, estimated_buffer_delay.ToMilliseconds(), 15);
+
+ // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
+ // detection.
+ RunXBursts(1,
+ packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+
+ // After draining the buffers we are back in a normal state.
+ int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(7, bursts_until_not_draining);
+
+ // After draining the buffer additionally we detect an underuse.
+ int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(5, bursts_until_underusing);
+}
+
+TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance100Kbit) {
+ int packets_per_burst = 1;
+ QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(100);
+ QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(50);
+ QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(50);
+
+ int overuse_signals = Run100000Samples(packets_per_burst,
+ mean,
+ standard_deviation);
+ EXPECT_GE(1, overuse_signals);
+
+ // Simulate a higher send pace, that is too high.
+ // With current tuning we require 4 updates with 50 milliseconds
+ // before detection.
+ // Resulting in a minimal buffer build up of 200 ms.
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst,
+ &estimated_buffer_delay);
+ EXPECT_GE(4, bursts_until_overuse);
+ EXPECT_NEAR(300, estimated_buffer_delay.ToMilliseconds(), 150);
+
+ // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
+ // detection.
+ RunXBursts(1,
+ packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+
+ // After draining the buffers we are back in a normal state.
+ int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(5, bursts_until_not_draining);
+
+ // After draining the buffer additionally we detect an underuse.
+ int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(5, bursts_until_underusing);
+}
+
+TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance1Mbit) {
+ int packets_per_burst = 1;
+ QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
+ QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5);
+ QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(5);
+
+ int overuse_signals = Run100000Samples(packets_per_burst,
+ mean,
+ standard_deviation);
+ EXPECT_GE(1, overuse_signals);
+
+ // Simulate a higher send pace, that is too high.
+ // With current tuning we require 4 updates with 5 millisecond
+ // before detection.
+ // Resulting in a minimal buffer build up of 20 ms.
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst,
+ &estimated_buffer_delay);
+ EXPECT_GE(4, bursts_until_overuse);
+ EXPECT_NEAR(30, estimated_buffer_delay.ToMilliseconds(), 15);
+
+ // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
+ // detection.
+ RunXBursts(10,
+ packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+
+ // After draining the buffers we are back in a normal state.
+ int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(14, bursts_until_not_draining);
+
+ // After draining the buffer additionally we detect an underuse.
+ int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(4, bursts_until_underusing);
+}
+
+TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance1Mbit) {
+ int packets_per_burst = 1;
+ QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
+ QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1);
+ QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(1);
+
+ int overuse_signals = Run100000Samples(packets_per_burst,
+ mean,
+ standard_deviation);
+ EXPECT_GE(1, overuse_signals);
+
+ // Simulate a higher send pace, that is too high.
+ // With current tuning we require 6 updates with 1 millisecond
+ // before detection.
+ // Resulting in a minimal buffer build up of 6 ms.
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst,
+ &estimated_buffer_delay);
+ EXPECT_GE(6, bursts_until_overuse);
+ EXPECT_NEAR(8, estimated_buffer_delay.ToMilliseconds(), 3);
+
+ // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
+ // detection.
+ RunXBursts(10,
+ packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+
+ // After draining the buffers we are back in a normal state.
+ int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(16, bursts_until_not_draining);
+
+ // After draining the buffer additionally we detect an underuse.
+ int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(4, bursts_until_underusing);
+}
+
+TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance20Mbit) {
+ int packets_per_burst = 2;
+ QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1);
+ QuicTime::Delta standard_deviation = QuicTime::Delta::FromMicroseconds(500);
+ QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(500);
+
+ int overuse_signals = Run100000Samples(packets_per_burst,
+ mean,
+ standard_deviation);
+ EXPECT_GE(1, overuse_signals);
+
+ // Simulate a higher send pace, that is too high.
+ // With current tuning we require 4 updates with 500 microsecond
+ // before detection.
+ // Resulting in a minimal buffer build up of 2 ms.
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst,
+ &estimated_buffer_delay);
+ EXPECT_GE(4, bursts_until_overuse);
+ EXPECT_NEAR(3, estimated_buffer_delay.ToMilliseconds(), 2);
+
+ // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
+ // detection.
+ RunXBursts(100,
+ packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+
+ // After draining the buffers we are back in a normal state.
+ int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_NEAR(100, bursts_until_not_draining, 10);
+
+ // After draining the buffer additionally we detect an underuse.
+ int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(1, bursts_until_underusing);
+}
+
+TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance100Mbit) {
+ int packets_per_burst = 10;
+ QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1);
+ QuicTime::Delta standard_deviation = QuicTime::Delta::FromMicroseconds(500);
+ QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(500);
+
+ int overuse_signals = Run100000Samples(packets_per_burst,
+ mean,
+ standard_deviation);
+ EXPECT_GE(1, overuse_signals);
+
+ // Simulate a higher send pace, that is too high.
+ // With current tuning we require 4 updates with 500 microsecond
+ // before detection.
+ // Resulting in a minimal buffer build up of 2 ms.
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst,
+ &estimated_buffer_delay);
+ EXPECT_GE(4, bursts_until_overuse);
+ EXPECT_NEAR(3, estimated_buffer_delay.ToMilliseconds(), 2);
+
+ // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
+ // detection.
+ RunXBursts(100,
+ packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+
+ // After draining the buffers we are back in a normal state.
+ int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_NEAR(100, bursts_until_not_draining, 10);
+
+ // After draining the buffer additionally we detect an underuse.
+ int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(1, bursts_until_underusing);
+}
+
+TEST_F(InterArrivalOveruseDetectorTest, DISABLED_VeryHighVariance100Mbit) {
+ int packets_per_burst = 10;
+ QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1);
+ QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5);
+ QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(500);
+
+ // We get false overuse in this scenario due to that the standard deviation is
+ // higher than our mean and the fact that a delta time can't be negative. This
+ // results in an under estimated standard deviation in the estimator causing
+ // false detects.
+ int overuse_signals = Run100000Samples(packets_per_burst,
+ mean,
+ standard_deviation);
+ EXPECT_GE(2000, overuse_signals);
+
+ // Simulate a higher send pace, that is too high.
+ // With current tuning we require 25 updates with 500 microsecond
+ // before detection.
+ // Resulting in a minimal buffer build up of 12.5 ms.
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst,
+ &estimated_buffer_delay);
+ EXPECT_GE(17, bursts_until_overuse);
+ EXPECT_NEAR(22, estimated_buffer_delay.ToMilliseconds(), 15);
+
+ // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
+ // detection.
+ RunXBursts(100,
+ packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+
+ // After draining the buffers we are back in a normal state.
+ int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(117, bursts_until_not_draining);
+
+ // After draining the buffer additionally we detect an underuse.
+ int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(1, bursts_until_underusing);
+}
+
+//
+// Tests simulating big drop in bitrate.
+//
+
+TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance1MbitTo100Kbit) {
+ int packets_per_burst = 1;
+ QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
+ QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1);
+ QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(100);
+
+ int overuse_signals = Run100000Samples(packets_per_burst,
+ mean,
+ standard_deviation);
+ EXPECT_GE(1, overuse_signals);
+
+ // Simulate a higher send pace, that is too high.
+ // With current tuning we require 1 update with 100 millisecond
+ // before detection.
+ // Resulting in a minimal buffer build up of 100 ms.
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst,
+ &estimated_buffer_delay);
+ EXPECT_GE(1, bursts_until_overuse);
+ EXPECT_NEAR(104, estimated_buffer_delay.ToMilliseconds(), 3);
+
+ // Back off 20% lower than estimate to drain.
+ mean = QuicTime::Delta::FromMilliseconds(100);
+ drift_per_burst = QuicTime::Delta::FromMilliseconds(20);
+
+ // After draining the buffers we are back in a normal state.
+ int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(5, bursts_until_not_draining);
+
+ // After draining the buffer additionally we detect an underuse.
+ int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(3, bursts_until_underusing);
+}
+
+TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance20MbitTo1Mbit) {
+ int packets_per_burst = 2;
+ QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1);
+ QuicTime::Delta standard_deviation = QuicTime::Delta::FromMicroseconds(500);
+ QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(10);
+
+ int overuse_signals = Run100000Samples(packets_per_burst,
+ mean,
+ standard_deviation);
+ EXPECT_GE(1, overuse_signals);
+
+ // Simulate a higher send pace, that is too high.
+ // With current tuning we require 1 update with 10 milliseconds
+ // before detection.
+ // Resulting in a minimal buffer build up of 10 ms.
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst,
+ &estimated_buffer_delay);
+ EXPECT_GE(1, bursts_until_overuse);
+ EXPECT_NEAR(12, estimated_buffer_delay.ToMilliseconds(), 2);
+
+ // Back off 20% lower than estimate to drain.
+ mean = QuicTime::Delta::FromMilliseconds(10);
+ drift_per_burst = QuicTime::Delta::FromMilliseconds(2);
+
+ // After draining the buffers we are back in a normal state.
+ int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(5, bursts_until_not_draining);
+
+ // After draining the buffer additionally we detect an underuse.
+ int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(3, bursts_until_underusing);
+}
+
+//
+// Tests that we can detect slow drifts.
+//
+
+TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance1MbitSmallSteps) {
+ int packets_per_burst = 1;
+ QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
+ QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1);
+ QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(100);
+
+ int overuse_signals = Run100000Samples(packets_per_burst,
+ mean,
+ standard_deviation);
+ EXPECT_GE(1, overuse_signals);
+
+ // Simulate a higher send pace, that is too high.
+ // With current tuning we require 41 updates with 100 microseconds before
+ // detection.
+ // Resulting in a minimal buffer build up of 4.1 ms.
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst,
+ &estimated_buffer_delay);
+ EXPECT_GE(41, bursts_until_overuse);
+ EXPECT_NEAR(7, estimated_buffer_delay.ToMilliseconds(), 3);
+
+ // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
+ // detection.
+ RunXBursts(10,
+ packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+
+ // After draining the buffers we are back in a normal state.
+ int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(29, bursts_until_not_draining);
+
+ // After draining the buffer additionally we detect an underuse.
+ int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(71, bursts_until_underusing);
+}
+
+TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance1MbitTinySteps) {
+ int packets_per_burst = 1;
+ QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
+ QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1);
+ QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(10);
+
+ int overuse_signals = Run100000Samples(packets_per_burst,
+ mean,
+ standard_deviation);
+ EXPECT_GE(1, overuse_signals);
+
+ // Simulate a higher send pace, that is too high.
+ // With current tuning we require 345 updates with 10 microseconds before
+ // detection.
+ // Resulting in a minimal buffer build up of 3.45 ms.
+ QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero());
+ int bursts_until_overuse = RunUntilOveruse(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst,
+ &estimated_buffer_delay);
+ EXPECT_GE(345, bursts_until_overuse);
+ EXPECT_NEAR(7, estimated_buffer_delay.ToMilliseconds(), 3);
+
+ // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before
+ // detection.
+ RunXBursts(10,
+ packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+
+ // After draining the buffers we are back in a normal state.
+ int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(18, bursts_until_not_draining);
+
+ // After draining the buffer additionally we detect an underuse.
+ int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(683, bursts_until_underusing);
+}
+
+//
+// Tests simulating starting with full buffers.
+//
+
+TEST_F(InterArrivalOveruseDetectorTest,
+ DISABLED_StartedWithFullBuffersHighVariance1Mbit) {
+ int packets_per_burst = 1;
+ QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
+ QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5);
+ QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(5);
+
+ int overuse_signals = Run100000Samples(packets_per_burst,
+ mean,
+ standard_deviation);
+ EXPECT_GE(1, overuse_signals);
+
+ // Simulate a lower send pace.
+ // Draining the buffer until we detect an underuse.
+ int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(6, bursts_until_underusing);
+
+ // After draining the buffers we are back in a normal state.
+ drift_per_burst = QuicTime::Delta::FromMilliseconds(0);
+ int bursts_until_steady = RunUntilSteady(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(6, bursts_until_steady);
+}
+
+TEST_F(InterArrivalOveruseDetectorTest,
+ DISABLED_StartedWithFullBuffersHighVariance1MbitSlowDrift) {
+ int packets_per_burst = 1;
+ QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
+ QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5);
+ QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(1);
+
+ int overuse_signals = Run100000Samples(packets_per_burst,
+ mean,
+ standard_deviation);
+ EXPECT_GE(1, overuse_signals);
+
+ // Simulate a faster receive pace.
+ // Draining the buffer until we detect an underuse.
+ int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(21, bursts_until_underusing);
+
+ // Simulate an RTT of 100 ms. Hence underusing for additional 100 ms before
+ // detection.
+ drift_per_burst = QuicTime::Delta::FromMilliseconds(-1);
+ RunXBursts(10,
+ packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+
+ // After draining the buffers we are back in a normal state.
+ drift_per_burst = QuicTime::Delta::FromMilliseconds(0);
+ int bursts_until_steady = RunUntilSteady(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(4, bursts_until_steady);
+}
+
+TEST_F(InterArrivalOveruseDetectorTest,
+ DISABLED_StartedWithFullBuffersLowVariance1Mbit) {
+ int packets_per_burst = 1;
+ QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10);
+ QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1);
+ QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(1);
+
+ int overuse_signals = Run100000Samples(packets_per_burst,
+ mean,
+ standard_deviation);
+ EXPECT_GE(1, overuse_signals);
+
+ // Simulate a lower send pace.
+ // Draining the buffer until we detect an underuse.
+ int bursts_until_underusing = RunUntilUnderusing(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(5, bursts_until_underusing);
+
+ // Simulate an RTT of 100 ms. Hence underusing for additional 100 ms before
+ // detection.
+ drift_per_burst = QuicTime::Delta::FromMilliseconds(-1);
+ RunXBursts(10,
+ packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+
+ // After draining the buffers we are back in a normal state.
+ drift_per_burst = QuicTime::Delta::FromMilliseconds(0);
+ int bursts_until_steady = RunUntilSteady(packets_per_burst,
+ mean,
+ standard_deviation,
+ drift_per_burst);
+ EXPECT_GE(41, bursts_until_steady);
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/congestion_control/inter_arrival_probe.cc b/net/quic/congestion_control/inter_arrival_probe.cc
new file mode 100644
index 0000000..6d4c073
--- /dev/null
+++ b/net/quic/congestion_control/inter_arrival_probe.cc
@@ -0,0 +1,117 @@
+// 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 "net/quic/congestion_control/inter_arrival_probe.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace {
+const int kProbeSizePackets = 10;
+const net::QuicByteCount kMinPacketSize = 500;
+const int64 kDefaultBytesPerSecond = 40000;
+const float kUncertainScaleFactor = 0.5; // TODO(pwestin): revisit this factor.
+}
+
+namespace net {
+
+InterArrivalProbe::InterArrivalProbe()
+ : estimate_available_(false),
+ available_channel_estimate_(QuicBandwidth::Zero()),
+ unacked_data_(0) {
+}
+
+InterArrivalProbe::~InterArrivalProbe() {
+}
+
+bool InterArrivalProbe::GetEstimate(QuicBandwidth* available_channel_estimate) {
+ if (!estimate_available_) {
+ return false;
+ }
+ *available_channel_estimate = available_channel_estimate_;
+ return true;
+}
+
+void InterArrivalProbe::OnSentPacket(QuicByteCount bytes) {
+ if (!estimate_available_) {
+ unacked_data_ += bytes;
+ }
+}
+
+void InterArrivalProbe::OnAcknowledgedPacket(QuicByteCount bytes) {
+ if (!estimate_available_) {
+ DCHECK_LE(bytes, unacked_data_);
+ unacked_data_ -= bytes;
+ }
+}
+
+QuicByteCount InterArrivalProbe::GetAvailableCongestionWindow() {
+ if (estimate_available_) {
+ return 0;
+ }
+ return (kProbeSizePackets * kMaxPacketSize) - unacked_data_;
+}
+
+void InterArrivalProbe::OnIncomingFeedback(
+ QuicPacketSequenceNumber sequence_number,
+ QuicByteCount bytes_sent,
+ QuicTime time_sent,
+ QuicTime time_received) {
+ if (estimate_available_) {
+ return;
+ }
+
+ if (available_channel_estimator_.get() == NULL) {
+ if (bytes_sent < kMinPacketSize) {
+ // Packet too small to start the probe phase.
+ return;
+ }
+ first_sequence_number_ = sequence_number;
+ available_channel_estimator_.reset(new AvailableChannelEstimator(
+ sequence_number, time_sent, time_received));
+ return;
+ }
+
+ available_channel_estimator_->OnIncomingFeedback(sequence_number,
+ bytes_sent,
+ time_sent,
+ time_received);
+ if (sequence_number < kProbeSizePackets - 1 + first_sequence_number_) {
+ // We need more feedback before we have a probe estimate.
+ return;
+ }
+ // Get the current estimated available channel capacity.
+ // available_channel_estimate is invalid if kAvailableChannelEstimateUnknown
+ // is returned.
+ QuicBandwidth available_channel_estimate = QuicBandwidth::Zero();
+ AvailableChannelEstimateState available_channel_estimate_state =
+ available_channel_estimator_->GetAvailableChannelEstimate(
+ &available_channel_estimate);
+ switch (available_channel_estimate_state) {
+ case kAvailableChannelEstimateUnknown:
+ // Backup when we miss our probe.
+ available_channel_estimate_ =
+ QuicBandwidth::FromBytesPerSecond(kDefaultBytesPerSecond);
+ break;
+ case kAvailableChannelEstimateUncertain:
+ available_channel_estimate_ =
+ available_channel_estimate.Scale(kUncertainScaleFactor);
+ break;
+ case kAvailableChannelEstimateGood:
+ available_channel_estimate_ = available_channel_estimate;
+ break;
+ case kAvailableChannelEstimateSenderLimited:
+ available_channel_estimate_ =
+ std::max(available_channel_estimate,
+ QuicBandwidth::FromBytesPerSecond(kDefaultBytesPerSecond));
+ break;
+ }
+ estimate_available_ = true;
+ available_channel_estimator_.reset(NULL);
+ DLOG(INFO) << "Probe estimate:"
+ << available_channel_estimate_.ToKBitsPerSecond()
+ << " Kbits/s";
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/inter_arrival_probe.h b/net/quic/congestion_control/inter_arrival_probe.h
new file mode 100644
index 0000000..5788a68
--- /dev/null
+++ b/net/quic/congestion_control/inter_arrival_probe.h
@@ -0,0 +1,58 @@
+// 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.
+
+// Class that handle the initial probing phase of inter arrival congestion
+// control.
+#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_PROBE_H_
+#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_PROBE_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/net_export.h"
+#include "net/quic/congestion_control/available_channel_estimator.h"
+#include "net/quic/quic_bandwidth.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE InterArrivalProbe {
+ public:
+ InterArrivalProbe();
+ ~InterArrivalProbe();
+
+ // Call every time a packet is sent to the network.
+ void OnSentPacket(QuicByteCount bytes);
+
+ // Call once for each sent packet that we receive an acknowledgement from
+ // the peer for.
+ void OnAcknowledgedPacket(QuicByteCount bytes);
+
+ // Call to get the number of bytes that can be sent as part of this probe.
+ QuicByteCount GetAvailableCongestionWindow();
+
+ // Call once for each sent packet we receive a congestion feedback from the
+ // peer for.
+ // If a peer sends both and ack and feedback for a sent packet, both
+ // OnAcknowledgedPacket and OnIncomingFeedback should be called.
+ void OnIncomingFeedback(QuicPacketSequenceNumber sequence_number,
+ QuicByteCount bytes_sent,
+ QuicTime time_sent,
+ QuicTime time_received);
+
+ // Returns false as long as we are probing, available_channel_estimate is
+ // invalid during that time. When the probe is completed this function return
+ // true and available_channel_estimate contains the estimate.
+ bool GetEstimate(QuicBandwidth* available_channel_estimate);
+
+ private:
+ scoped_ptr<AvailableChannelEstimator> available_channel_estimator_;
+ QuicPacketSequenceNumber first_sequence_number_;
+ bool estimate_available_;
+ QuicBandwidth available_channel_estimate_;
+ QuicByteCount unacked_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterArrivalProbe);
+};
+
+} // namespace net
+#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_PROBE_H_
diff --git a/net/quic/congestion_control/inter_arrival_probe_test.cc b/net/quic/congestion_control/inter_arrival_probe_test.cc
new file mode 100644
index 0000000..18b97d3
--- /dev/null
+++ b/net/quic/congestion_control/inter_arrival_probe_test.cc
@@ -0,0 +1,81 @@
+// 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 "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/congestion_control/inter_arrival_probe.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class InterArrivalProbeTest : public ::testing::Test {
+ protected:
+ InterArrivalProbeTest() : start_(QuicTime::Zero()) {
+ }
+
+ InterArrivalProbe probe_;
+ QuicTime start_;
+};
+
+TEST_F(InterArrivalProbeTest, CongestionWindow) {
+ for (size_t i = 0; i < 10; i++) {
+ probe_.OnSentPacket(kMaxPacketSize);
+ EXPECT_EQ((9 - i) * kMaxPacketSize, probe_.GetAvailableCongestionWindow());
+ }
+ probe_.OnAcknowledgedPacket(kMaxPacketSize);
+ EXPECT_EQ(kMaxPacketSize, probe_.GetAvailableCongestionWindow());
+
+ probe_.OnSentPacket(kMaxPacketSize);
+ EXPECT_EQ(0u, probe_.GetAvailableCongestionWindow());
+}
+
+TEST_F(InterArrivalProbeTest, Estimate) {
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicByteCount bytes_sent = kMaxPacketSize;
+ QuicTime time_received = start_.Add(QuicTime::Delta::FromMilliseconds(10));
+ QuicTime time_sent = start_.Add(QuicTime::Delta::FromMilliseconds(1));
+ QuicBandwidth available_channel_estimate = QuicBandwidth::Zero();
+
+ for (size_t i = 0; i < 10; ++i) {
+ EXPECT_FALSE(probe_.GetEstimate(&available_channel_estimate));
+
+ probe_.OnIncomingFeedback(sequence_number++,
+ bytes_sent,
+ time_sent,
+ time_received);
+ time_sent = time_sent.Add(QuicTime::Delta::FromMilliseconds(1));
+ time_received = time_received.Add(QuicTime::Delta::FromMilliseconds(10));
+ }
+ EXPECT_TRUE(probe_.GetEstimate(&available_channel_estimate));
+ EXPECT_EQ(kMaxPacketSize * 100,
+ static_cast<uint64>(available_channel_estimate.ToBytesPerSecond()));
+}
+
+TEST_F(InterArrivalProbeTest, EstimateWithLoss) {
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicByteCount bytes_sent = kMaxPacketSize;
+ QuicTime time_received = start_.Add(QuicTime::Delta::FromMilliseconds(10));
+ QuicTime time_sent = start_.Add(QuicTime::Delta::FromMilliseconds(1));
+ QuicBandwidth available_channel_estimate = QuicBandwidth::Zero();
+
+ for (size_t i = 0; i < 6; ++i) {
+ EXPECT_FALSE(probe_.GetEstimate(&available_channel_estimate));
+
+ probe_.OnIncomingFeedback(sequence_number,
+ bytes_sent,
+ time_sent,
+ time_received);
+ sequence_number += 2;
+ time_sent = time_sent.Add(QuicTime::Delta::FromMilliseconds(1));
+ time_received = time_received.Add(QuicTime::Delta::FromMilliseconds(10));
+ }
+ EXPECT_TRUE(probe_.GetEstimate(&available_channel_estimate));
+ EXPECT_EQ(kMaxPacketSize * 50,
+ static_cast<uint64>(available_channel_estimate.ToBytesPerSecond()));
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/congestion_control/inter_arrival_receiver.cc b/net/quic/congestion_control/inter_arrival_receiver.cc
new file mode 100644
index 0000000..770b287
--- /dev/null
+++ b/net/quic/congestion_control/inter_arrival_receiver.cc
@@ -0,0 +1,48 @@
+// 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 "net/quic/congestion_control/inter_arrival_receiver.h"
+
+#include "base/basictypes.h"
+
+namespace net {
+
+InterArrivalReceiver::InterArrivalReceiver()
+ : accumulated_number_of_recoverd_lost_packets_(0) {
+}
+
+InterArrivalReceiver::~InterArrivalReceiver() {
+}
+
+bool InterArrivalReceiver::GenerateCongestionFeedback(
+ QuicCongestionFeedbackFrame* feedback) {
+ if (received_packet_times_.size() <= 1) {
+ // Don't waste resources by sending a feedback frame for only one packet.
+ return false;
+ }
+ feedback->type = kInterArrival;
+ feedback->inter_arrival.accumulated_number_of_lost_packets =
+ accumulated_number_of_recoverd_lost_packets_;
+
+ // Copy our current receive set to our feedback message, we will not resend
+ // this data if it is lost.
+ feedback->inter_arrival.received_packet_times = received_packet_times_;
+
+ // Prepare for the next set of arriving packets by clearing our current set.
+ received_packet_times_.clear();
+ return true;
+}
+
+void InterArrivalReceiver::RecordIncomingPacket(
+ QuicByteCount /*bytes*/,
+ QuicPacketSequenceNumber sequence_number,
+ QuicTime timestamp,
+ bool revived) {
+ if (revived) {
+ ++accumulated_number_of_recoverd_lost_packets_;
+ }
+ received_packet_times_.insert(std::make_pair(sequence_number, timestamp));
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/inter_arrival_receiver.h b/net/quic/congestion_control/inter_arrival_receiver.h
new file mode 100644
index 0000000..a9de62cb
--- /dev/null
+++ b/net/quic/congestion_control/inter_arrival_receiver.h
@@ -0,0 +1,46 @@
+// 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.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_RECEIVER_H_
+#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_RECEIVER_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "net/base/net_export.h"
+#include "net/quic/congestion_control/receive_algorithm_interface.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE InterArrivalReceiver
+ : public ReceiveAlgorithmInterface {
+ public:
+ InterArrivalReceiver();
+ virtual ~InterArrivalReceiver();
+
+ // Start implementation of ReceiveAlgorithmInterface.
+ virtual bool GenerateCongestionFeedback(
+ QuicCongestionFeedbackFrame* feedback) OVERRIDE;
+
+ virtual void RecordIncomingPacket(QuicByteCount bytes,
+ QuicPacketSequenceNumber sequence_number,
+ QuicTime timestamp,
+ bool revived) OVERRIDE;
+ // End implementation of ReceiveAlgorithmInterface.
+
+ private:
+ // We need to keep track of FEC recovered packets.
+ int accumulated_number_of_recoverd_lost_packets_;
+
+ // The set of received packets since the last feedback was sent, along with
+ // their arrival times.
+ TimeMap received_packet_times_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterArrivalReceiver);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_RECEIVER_H_
diff --git a/net/quic/congestion_control/inter_arrival_receiver_test.cc b/net/quic/congestion_control/inter_arrival_receiver_test.cc
new file mode 100644
index 0000000..927fb6d9
--- /dev/null
+++ b/net/quic/congestion_control/inter_arrival_receiver_test.cc
@@ -0,0 +1,55 @@
+// 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 "base/logging.h"
+#include "net/quic/congestion_control/inter_arrival_receiver.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class InterArrivalReceiverTest : public ::testing::Test {
+ protected:
+ InterArrivalReceiver receiver_;
+ MockClock clock_;
+};
+
+TEST_F(InterArrivalReceiverTest, SimpleReceiver) {
+ QuicTime start = clock_.ApproximateNow();
+ QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10);
+ clock_.AdvanceTime(received_delta);
+ QuicTime receive_timestamp = clock_.ApproximateNow();
+ receiver_.RecordIncomingPacket(1, 1, receive_timestamp, false);
+
+ QuicCongestionFeedbackFrame feedback;
+ ASSERT_FALSE(receiver_.GenerateCongestionFeedback(&feedback));
+
+ clock_.AdvanceTime(received_delta);
+ receive_timestamp = clock_.ApproximateNow();
+ // Packet not received; but rather revived by FEC.
+ receiver_.RecordIncomingPacket(1, 2, receive_timestamp, true);
+ clock_.AdvanceTime(received_delta);
+ receive_timestamp = clock_.ApproximateNow();
+ receiver_.RecordIncomingPacket(1, 3, receive_timestamp, false);
+
+ ASSERT_TRUE(receiver_.GenerateCongestionFeedback(&feedback));
+
+ EXPECT_EQ(kInterArrival, feedback.type);
+ EXPECT_EQ(1, feedback.inter_arrival.accumulated_number_of_lost_packets);
+ EXPECT_EQ(3u, feedback.inter_arrival.received_packet_times.size());
+ TimeMap::iterator it = feedback.inter_arrival.received_packet_times.begin();
+ EXPECT_EQ(1u, it->first);
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), it->second.Subtract(start));
+ it = feedback.inter_arrival.received_packet_times.begin();
+ it++;
+ EXPECT_EQ(2u, it->first);
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(20), it->second.Subtract(start));
+ it++;
+ EXPECT_EQ(3u, it->first);
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(30), it->second.Subtract(start));
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/congestion_control/inter_arrival_sender.cc b/net/quic/congestion_control/inter_arrival_sender.cc
new file mode 100644
index 0000000..77b9af0
--- /dev/null
+++ b/net/quic/congestion_control/inter_arrival_sender.cc
@@ -0,0 +1,449 @@
+// 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 "net/quic/congestion_control/inter_arrival_sender.h"
+
+namespace {
+const int64 kProbeBitrateKBytesPerSecond = 1200; // 9.6 Mbit/s
+const float kPacketLossBitrateReduction = 0.7f;
+const float kUncertainSafetyMargin = 0.7f;
+const float kMaxBitrateReduction = 0.9f;
+const float kMinBitrateReduction = 0.05f;
+const uint64 kMinBitrateKbit = 10;
+const int kInitialRttMs = 60; // At a typical RTT 60 ms.
+}
+
+namespace net {
+
+InterArrivalSender::InterArrivalSender(const QuicClock* clock)
+ : probing_(true),
+ current_bandwidth_(QuicBandwidth::Zero()),
+ smoothed_rtt_(QuicTime::Delta::Zero()),
+ channel_estimator_(new ChannelEstimator()),
+ bitrate_ramp_up_(new InterArrivalBitrateRampUp(clock)),
+ overuse_detector_(new InterArrivalOveruseDetector()),
+ probe_(new InterArrivalProbe()),
+ state_machine_(new InterArrivalStateMachine(clock)),
+ paced_sender_(new PacedSender(QuicBandwidth::FromKBytesPerSecond(
+ kProbeBitrateKBytesPerSecond))),
+ accumulated_number_of_lost_packets_(0),
+ bandwidth_usage_state_(kBandwidthSteady),
+ back_down_time_(QuicTime::Zero()),
+ back_down_bandwidth_(QuicBandwidth::Zero()),
+ back_down_congestion_delay_(QuicTime::Delta::Zero()) {
+}
+
+InterArrivalSender::~InterArrivalSender() {
+}
+
+void InterArrivalSender::OnIncomingQuicCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& feedback,
+ QuicTime feedback_receive_time,
+ QuicBandwidth sent_bandwidth,
+ const SentPacketsMap& sent_packets) {
+ DCHECK(feedback.type == kInterArrival);
+
+ if (feedback.type != kInterArrival) {
+ return;
+ }
+ TimeMap::const_iterator received_it;
+ for (received_it = feedback.inter_arrival.received_packet_times.begin();
+ received_it != feedback.inter_arrival.received_packet_times.end();
+ ++received_it) {
+ QuicPacketSequenceNumber sequence_number = received_it->first;
+
+ SentPacketsMap::const_iterator sent_it = sent_packets.find(sequence_number);
+ if (sent_it == sent_packets.end()) {
+ // Too old data; ignore and move forward.
+ DLOG(INFO) << "Too old feedback move forward, sequence_number:"
+ << sequence_number;
+ continue;
+ }
+ QuicTime time_received = received_it->second;
+ QuicTime time_sent = sent_it->second->SendTimestamp();
+ QuicByteCount bytes_sent = sent_it->second->BytesSent();
+
+ channel_estimator_->OnAcknowledgedPacket(
+ sequence_number, bytes_sent, time_sent, time_received);
+ if (probing_) {
+ probe_->OnIncomingFeedback(
+ sequence_number, bytes_sent, time_sent, time_received);
+ } else {
+ bool last_of_send_time = false;
+ SentPacketsMap::const_iterator next_sent_it = ++sent_it;
+ if (next_sent_it == sent_packets.end()) {
+ // No more sent packets; hence this must be the last.
+ last_of_send_time = true;
+ } else {
+ if (time_sent != next_sent_it->second->SendTimestamp()) {
+ // Next sent packet have a different send time.
+ last_of_send_time = true;
+ }
+ }
+ overuse_detector_->OnAcknowledgedPacket(
+ sequence_number, time_sent, last_of_send_time, time_received);
+ }
+ }
+ if (probing_) {
+ probing_ = ProbingPhase(feedback_receive_time);
+ return;
+ }
+
+ bool packet_loss_event = false;
+ if (accumulated_number_of_lost_packets_ !=
+ feedback.inter_arrival.accumulated_number_of_lost_packets) {
+ accumulated_number_of_lost_packets_ =
+ feedback.inter_arrival.accumulated_number_of_lost_packets;
+ packet_loss_event = true;
+ }
+ InterArrivalState state = state_machine_->GetInterArrivalState();
+
+ if (state == kInterArrivalStatePacketLoss ||
+ state == kInterArrivalStateCompetingTcpFLow) {
+ if (packet_loss_event) {
+ if (!state_machine_->PacketLossEvent()) {
+ // Less than one RTT since last PacketLossEvent.
+ return;
+ }
+ EstimateBandwidthAfterLossEvent(feedback_receive_time);
+ } else {
+ EstimateNewBandwidth(feedback_receive_time, sent_bandwidth);
+ }
+ return;
+ }
+ EstimateDelayBandwidth(feedback_receive_time, sent_bandwidth);
+}
+
+bool InterArrivalSender::ProbingPhase(QuicTime feedback_receive_time) {
+ QuicBandwidth available_channel_estimate = QuicBandwidth::Zero();
+ if (!probe_->GetEstimate(&available_channel_estimate)) {
+ // Continue probing phase.
+ return true;
+ }
+ QuicBandwidth channel_estimate = QuicBandwidth::Zero();
+ ChannelEstimateState channel_estimator_state =
+ channel_estimator_->GetChannelEstimate(&channel_estimate);
+
+ QuicBandwidth new_rate =
+ available_channel_estimate.Scale(kUncertainSafetyMargin);
+
+ switch (channel_estimator_state) {
+ case kChannelEstimateUnknown:
+ channel_estimate = available_channel_estimate;
+ break;
+ case kChannelEstimateUncertain:
+ channel_estimate = channel_estimate.Scale(kUncertainSafetyMargin);
+ break;
+ case kChannelEstimateGood:
+ // Do nothing.
+ break;
+ }
+ new_rate = std::max(new_rate,
+ QuicBandwidth::FromKBitsPerSecond(kMinBitrateKbit));
+
+ bitrate_ramp_up_->Reset(new_rate, available_channel_estimate,
+ channel_estimate);
+
+ current_bandwidth_ = new_rate;
+ paced_sender_->UpdateBandwidthEstimate(feedback_receive_time, new_rate);
+ DLOG(INFO) << "Probe result; new rate:"
+ << new_rate.ToKBitsPerSecond() << " Kbits/s "
+ << " available estimate:"
+ << available_channel_estimate.ToKBitsPerSecond() << " Kbits/s "
+ << " channel estimate:"
+ << channel_estimate.ToKBitsPerSecond() << " Kbits/s ";
+ return false;
+}
+
+void InterArrivalSender::OnIncomingAck(
+ QuicPacketSequenceNumber /*acked_sequence_number*/,
+ QuicByteCount acked_bytes,
+ QuicTime::Delta rtt) {
+ DCHECK(!rtt.IsZero());
+ DCHECK(!rtt.IsInfinite());
+ if (smoothed_rtt_.IsZero()) {
+ smoothed_rtt_ = rtt;
+ } else {
+ smoothed_rtt_ = QuicTime::Delta::FromMicroseconds(
+ (smoothed_rtt_.ToMicroseconds() * 3 + rtt.ToMicroseconds()) / 4);
+ }
+ state_machine_->set_rtt(SmoothedRtt());
+ if (probing_) {
+ probe_->OnAcknowledgedPacket(acked_bytes);
+ }
+}
+
+void InterArrivalSender::OnIncomingLoss(QuicTime ack_receive_time) {
+ // Packet loss was reported.
+ if (!probing_) {
+ if (!state_machine_->PacketLossEvent()) {
+ // Less than one RTT since last PacketLossEvent.
+ return;
+ }
+ // Calculate new pace rate.
+ EstimateBandwidthAfterLossEvent(ack_receive_time);
+ }
+}
+
+void InterArrivalSender::SentPacket(QuicTime sent_time,
+ QuicPacketSequenceNumber sequence_number,
+ QuicByteCount bytes,
+ Retransmission /*retransmit*/) {
+ if (probing_) {
+ probe_->OnSentPacket(bytes);
+ }
+ paced_sender_->SentPacket(sent_time, bytes);
+}
+
+void InterArrivalSender::AbandoningPacket(
+ QuicPacketSequenceNumber /*sequence_number*/,
+ QuicByteCount abandoned_bytes) {
+ // TODO(pwestin): use for out outer_congestion_window_ logic.
+ if (probing_) {
+ probe_->OnAcknowledgedPacket(abandoned_bytes);
+ }
+}
+
+QuicTime::Delta InterArrivalSender::TimeUntilSend(
+ QuicTime now,
+ Retransmission /*retransmit*/,
+ HasRetransmittableData has_retransmittable_data) {
+ // TODO(pwestin): implement outer_congestion_window_ logic.
+ QuicTime::Delta outer_window = QuicTime::Delta::Zero();
+
+ if (probing_) {
+ if (has_retransmittable_data == HAS_RETRANSMITTABLE_DATA &&
+ probe_->GetAvailableCongestionWindow() == 0) {
+ outer_window = QuicTime::Delta::Infinite();
+ }
+ }
+ return paced_sender_->TimeUntilSend(now, outer_window);
+}
+
+void InterArrivalSender::EstimateDelayBandwidth(QuicTime feedback_receive_time,
+ QuicBandwidth sent_bandwidth) {
+ QuicTime::Delta estimated_congestion_delay = QuicTime::Delta::Zero();
+ BandwidthUsage new_bandwidth_usage_state =
+ overuse_detector_->GetState(&estimated_congestion_delay);
+
+ switch (new_bandwidth_usage_state) {
+ case kBandwidthDraining:
+ case kBandwidthUnderUsing:
+ // Hold our current bitrate.
+ break;
+ case kBandwidthOverUsing:
+ if (!state_machine_->IncreasingDelayEvent()) {
+ // Less than one RTT since last IncreasingDelayEvent.
+ return;
+ }
+ EstimateBandwidthAfterDelayEvent(feedback_receive_time,
+ estimated_congestion_delay);
+ break;
+ case kBandwidthSteady:
+ // Calculate new pace rate.
+ if (bandwidth_usage_state_ == kBandwidthDraining ||
+ bandwidth_usage_state_ == kBandwidthOverUsing) {
+ EstimateNewBandwidthAfterDraining(feedback_receive_time,
+ estimated_congestion_delay);
+ } else {
+ EstimateNewBandwidth(feedback_receive_time, sent_bandwidth);
+ }
+ break;
+ }
+ bandwidth_usage_state_ = new_bandwidth_usage_state;
+}
+
+QuicBandwidth InterArrivalSender::BandwidthEstimate() {
+ return current_bandwidth_;
+}
+
+QuicTime::Delta InterArrivalSender::SmoothedRtt() {
+ if (smoothed_rtt_.IsZero()) {
+ return QuicTime::Delta::FromMilliseconds(kInitialRttMs);
+ }
+ return smoothed_rtt_;
+}
+
+void InterArrivalSender::EstimateNewBandwidth(QuicTime feedback_receive_time,
+ QuicBandwidth sent_bandwidth) {
+ QuicBandwidth new_bandwidth = bitrate_ramp_up_->GetNewBitrate(sent_bandwidth);
+ if (current_bandwidth_ == new_bandwidth) {
+ return;
+ }
+ current_bandwidth_ = new_bandwidth;
+ state_machine_->IncreaseBitrateDecision();
+
+ QuicBandwidth channel_estimate = QuicBandwidth::Zero();
+ ChannelEstimateState channel_estimator_state =
+ channel_estimator_->GetChannelEstimate(&channel_estimate);
+
+ if (channel_estimator_state == kChannelEstimateGood) {
+ bitrate_ramp_up_->UpdateChannelEstimate(channel_estimate);
+ }
+ paced_sender_->UpdateBandwidthEstimate(feedback_receive_time,
+ current_bandwidth_);
+ DLOG(INFO) << "New bandwidth estimate in steady state:"
+ << current_bandwidth_.ToKBitsPerSecond()
+ << " Kbits/s";
+}
+
+// Did we drain the network buffers in our expected pace?
+void InterArrivalSender::EstimateNewBandwidthAfterDraining(
+ QuicTime feedback_receive_time,
+ QuicTime::Delta estimated_congestion_delay) {
+ if (current_bandwidth_ > back_down_bandwidth_) {
+ // Do nothing, our current bandwidth is higher than our bandwidth at the
+ // previous back down.
+ DLOG(INFO) << "Current bandwidth estimate is higher than before draining";
+ return;
+ }
+ if (estimated_congestion_delay >= back_down_congestion_delay_) {
+ // Do nothing, our estimated delay have increased.
+ DLOG(INFO) << "Current delay estimate is higher than before draining";
+ return;
+ }
+ DCHECK(back_down_time_.IsInitialized());
+ QuicTime::Delta buffer_reduction =
+ back_down_congestion_delay_.Subtract(estimated_congestion_delay);
+ QuicTime::Delta elapsed_time =
+ feedback_receive_time.Subtract(back_down_time_).Subtract(SmoothedRtt());
+
+ QuicBandwidth new_estimate = QuicBandwidth::Zero();
+ if (buffer_reduction >= elapsed_time) {
+ // We have drained more than the elapsed time... go back to our old rate.
+ new_estimate = back_down_bandwidth_;
+ } else {
+ float fraction_of_rate =
+ static_cast<float>(buffer_reduction.ToMicroseconds()) /
+ elapsed_time.ToMicroseconds(); // < 1.0
+
+ QuicBandwidth draining_rate = back_down_bandwidth_.Scale(fraction_of_rate);
+ QuicBandwidth max_estimated_draining_rate =
+ back_down_bandwidth_.Subtract(current_bandwidth_);
+ if (draining_rate > max_estimated_draining_rate) {
+ // We drained faster than our old send rate, go back to our old rate.
+ new_estimate = back_down_bandwidth_;
+ } else {
+ // Use our drain rate and our kMinBitrateReduction to go to our
+ // new estimate.
+ new_estimate = std::max(current_bandwidth_,
+ current_bandwidth_.Add(draining_rate).Scale(
+ 1.0f - kMinBitrateReduction));
+ DLOG(INFO) << "Draining calculation; current rate:"
+ << current_bandwidth_.ToKBitsPerSecond() << " Kbits/s "
+ << "draining rate:"
+ << draining_rate.ToKBitsPerSecond() << " Kbits/s "
+ << "new estimate:"
+ << new_estimate.ToKBitsPerSecond() << " Kbits/s "
+ << " buffer reduction:"
+ << buffer_reduction.ToMicroseconds() << " us "
+ << " elapsed time:"
+ << elapsed_time.ToMicroseconds() << " us ";
+ }
+ }
+ if (new_estimate == current_bandwidth_) {
+ return;
+ }
+
+ QuicBandwidth channel_estimate = QuicBandwidth::Zero();
+ ChannelEstimateState channel_estimator_state =
+ channel_estimator_->GetChannelEstimate(&channel_estimate);
+
+ // TODO(pwestin): we need to analyze channel_estimate too.
+ switch (channel_estimator_state) {
+ case kChannelEstimateUnknown:
+ channel_estimate = current_bandwidth_;
+ break;
+ case kChannelEstimateUncertain:
+ channel_estimate = channel_estimate.Scale(kUncertainSafetyMargin);
+ break;
+ case kChannelEstimateGood:
+ // Do nothing, estimate is accurate.
+ break;
+ }
+ bitrate_ramp_up_->Reset(new_estimate, back_down_bandwidth_, channel_estimate);
+ state_machine_->IncreaseBitrateDecision();
+ paced_sender_->UpdateBandwidthEstimate(feedback_receive_time, new_estimate);
+ current_bandwidth_ = new_estimate;
+ DLOG(INFO) << "New bandwidth estimate after draining:"
+ << new_estimate.ToKBitsPerSecond() << " Kbits/s";
+}
+
+void InterArrivalSender::EstimateBandwidthAfterDelayEvent(
+ QuicTime feedback_receive_time,
+ QuicTime::Delta estimated_congestion_delay) {
+ QuicByteCount estimated_byte_buildup =
+ current_bandwidth_.ToBytesPerPeriod(estimated_congestion_delay);
+
+ // To drain all build up buffer within one RTT we need to reduce the
+ // bitrate with the following.
+ // TODO(pwestin): this is a crude first implementation.
+ int64 draining_rate_per_rtt = (estimated_byte_buildup *
+ kNumMicrosPerSecond) / SmoothedRtt().ToMicroseconds();
+
+ float decrease_factor =
+ draining_rate_per_rtt / current_bandwidth_.ToBytesPerSecond();
+
+ decrease_factor = std::max(decrease_factor, kMinBitrateReduction);
+ decrease_factor = std::min(decrease_factor, kMaxBitrateReduction);
+ back_down_congestion_delay_ = estimated_congestion_delay;
+ QuicBandwidth new_target_bitrate =
+ current_bandwidth_.Scale(1.0f - decrease_factor);
+
+ // While in delay sensing mode send at least one packet per RTT.
+ QuicBandwidth min_delay_bitrate =
+ QuicBandwidth::FromBytesAndTimeDelta(kMaxPacketSize, SmoothedRtt());
+ new_target_bitrate = std::max(new_target_bitrate, min_delay_bitrate);
+
+ ResetCurrentBandwidth(feedback_receive_time, new_target_bitrate);
+
+ DLOG(INFO) << "New bandwidth estimate after delay event:"
+ << current_bandwidth_.ToKBitsPerSecond()
+ << " Kbits/s min delay bitrate:"
+ << min_delay_bitrate.ToKBitsPerSecond()
+ << " Kbits/s RTT:"
+ << SmoothedRtt().ToMicroseconds()
+ << " us";
+}
+
+void InterArrivalSender::EstimateBandwidthAfterLossEvent(
+ QuicTime feedback_receive_time) {
+ ResetCurrentBandwidth(feedback_receive_time,
+ current_bandwidth_.Scale(kPacketLossBitrateReduction));
+ DLOG(INFO) << "New bandwidth estimate after loss event:"
+ << current_bandwidth_.ToKBitsPerSecond()
+ << " Kbits/s";
+}
+
+void InterArrivalSender::ResetCurrentBandwidth(QuicTime feedback_receive_time,
+ QuicBandwidth new_rate) {
+ new_rate = std::max(new_rate,
+ QuicBandwidth::FromKBitsPerSecond(kMinBitrateKbit));
+ QuicBandwidth channel_estimate = QuicBandwidth::Zero();
+ ChannelEstimateState channel_estimator_state =
+ channel_estimator_->GetChannelEstimate(&channel_estimate);
+
+ switch (channel_estimator_state) {
+ case kChannelEstimateUnknown:
+ channel_estimate = current_bandwidth_;
+ break;
+ case kChannelEstimateUncertain:
+ channel_estimate = channel_estimate.Scale(kUncertainSafetyMargin);
+ break;
+ case kChannelEstimateGood:
+ // Do nothing.
+ break;
+ }
+ back_down_time_ = feedback_receive_time;
+ back_down_bandwidth_ = current_bandwidth_;
+ bitrate_ramp_up_->Reset(new_rate, current_bandwidth_, channel_estimate);
+ if (new_rate != current_bandwidth_) {
+ current_bandwidth_ = new_rate;
+ paced_sender_->UpdateBandwidthEstimate(feedback_receive_time,
+ current_bandwidth_);
+ state_machine_->DecreaseBitrateDecision();
+ }
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/inter_arrival_sender.h b/net/quic/congestion_control/inter_arrival_sender.h
new file mode 100644
index 0000000..afcafe8
--- /dev/null
+++ b/net/quic/congestion_control/inter_arrival_sender.h
@@ -0,0 +1,95 @@
+// 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.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_SENDER_H_
+#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_SENDER_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/net_export.h"
+#include "net/quic/congestion_control/channel_estimator.h"
+#include "net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h"
+#include "net/quic/congestion_control/inter_arrival_overuse_detector.h"
+#include "net/quic/congestion_control/inter_arrival_probe.h"
+#include "net/quic/congestion_control/inter_arrival_state_machine.h"
+#include "net/quic/congestion_control/paced_sender.h"
+#include "net/quic/congestion_control/send_algorithm_interface.h"
+#include "net/quic/quic_bandwidth.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE InterArrivalSender : public SendAlgorithmInterface {
+ public:
+ explicit InterArrivalSender(const QuicClock* clock);
+ virtual ~InterArrivalSender();
+
+ // Start implementation of SendAlgorithmInterface.
+ virtual void OnIncomingQuicCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& feedback,
+ QuicTime feedback_receive_time,
+ QuicBandwidth sent_bandwidth,
+ const SentPacketsMap& sent_packets) OVERRIDE;
+
+ virtual void OnIncomingAck(QuicPacketSequenceNumber acked_sequence_number,
+ QuicByteCount acked_bytes,
+ QuicTime::Delta rtt) OVERRIDE;
+
+ virtual void OnIncomingLoss(QuicTime ack_receive_time) OVERRIDE;
+
+ virtual void SentPacket(QuicTime sent_time,
+ QuicPacketSequenceNumber sequence_number,
+ QuicByteCount bytes,
+ Retransmission is_retransmit) OVERRIDE;
+
+ virtual void AbandoningPacket(QuicPacketSequenceNumber sequence_number,
+ QuicByteCount abandoned_bytes) OVERRIDE;
+
+ virtual QuicTime::Delta TimeUntilSend(
+ QuicTime now,
+ Retransmission is_retransmission,
+ HasRetransmittableData has_retransmittable_data) OVERRIDE;
+
+ virtual QuicBandwidth BandwidthEstimate() OVERRIDE;
+ virtual QuicTime::Delta SmoothedRtt() OVERRIDE;
+ // End implementation of SendAlgorithmInterface.
+
+ private:
+ void EstimateDelayBandwidth(QuicTime feedback_receive_time,
+ QuicBandwidth sent_bandwidth);
+ void EstimateNewBandwidth(QuicTime feedback_receive_time,
+ QuicBandwidth sent_bandwidth);
+ void EstimateNewBandwidthAfterDraining(
+ QuicTime feedback_receive_time,
+ QuicTime::Delta estimated_congestion_delay);
+ void EstimateBandwidthAfterLossEvent(QuicTime feedback_receive_time);
+ void EstimateBandwidthAfterDelayEvent(
+ QuicTime feedback_receive_time,
+ QuicTime::Delta estimated_congestion_delay);
+ void ResetCurrentBandwidth(QuicTime feedback_receive_time,
+ QuicBandwidth new_rate);
+ bool ProbingPhase(QuicTime feedback_receive_time);
+
+ bool probing_; // Are we currently in the probing phase?
+ QuicBandwidth current_bandwidth_;
+ QuicTime::Delta smoothed_rtt_;
+ scoped_ptr<ChannelEstimator> channel_estimator_;
+ scoped_ptr<InterArrivalBitrateRampUp> bitrate_ramp_up_;
+ scoped_ptr<InterArrivalOveruseDetector> overuse_detector_;
+ scoped_ptr<InterArrivalProbe> probe_;
+ scoped_ptr<InterArrivalStateMachine> state_machine_;
+ scoped_ptr<PacedSender> paced_sender_;
+ int accumulated_number_of_lost_packets_;
+ BandwidthUsage bandwidth_usage_state_;
+ QuicTime back_down_time_; // Time when we decided to back down.
+ QuicBandwidth back_down_bandwidth_; // Bandwidth before backing down.
+ QuicTime::Delta back_down_congestion_delay_; // Delay when backing down.
+
+ DISALLOW_COPY_AND_ASSIGN(InterArrivalSender);
+};
+
+} // namespace net
+#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_SENDER_H_
diff --git a/net/quic/congestion_control/inter_arrival_sender_test.cc b/net/quic/congestion_control/inter_arrival_sender_test.cc
new file mode 100644
index 0000000..28ac9b2
--- /dev/null
+++ b/net/quic/congestion_control/inter_arrival_sender_test.cc
@@ -0,0 +1,565 @@
+// 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 "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "net/quic/congestion_control/inter_arrival_sender.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class InterArrivalSenderTest : public ::testing::Test {
+ protected:
+ InterArrivalSenderTest()
+ : rtt_(QuicTime::Delta::FromMilliseconds(60)),
+ one_ms_(QuicTime::Delta::FromMilliseconds(1)),
+ one_s_(QuicTime::Delta::FromMilliseconds(1000)),
+ nine_ms_(QuicTime::Delta::FromMilliseconds(9)),
+ send_start_time_(send_clock_.Now()),
+ sender_(&send_clock_),
+ sequence_number_(1),
+ acked_sequence_number_(1),
+ feedback_sequence_number_(1) {
+ send_clock_.AdvanceTime(one_ms_);
+ receive_clock_.AdvanceTime(one_ms_);
+ }
+
+ ~InterArrivalSenderTest() {
+ STLDeleteValues(&sent_packets_);
+ }
+
+ void SendAvailableCongestionWindow() {
+ while (sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero()) {
+ QuicByteCount bytes_in_packet = kMaxPacketSize;
+ sent_packets_[sequence_number_] =
+ new class SendAlgorithmInterface::SentPacket(
+ bytes_in_packet, send_clock_.Now());
+
+ sender_.SentPacket(send_clock_.Now(), sequence_number_, bytes_in_packet,
+ NOT_RETRANSMISSION);
+ sequence_number_++;
+ }
+ EXPECT_FALSE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ }
+
+ void AckNPackets(int n) {
+ for (int i = 0; i < n; ++i) {
+ sender_.OnIncomingAck(acked_sequence_number_++, kMaxPacketSize, rtt_);
+ }
+ }
+
+ void SendDelaySpikeFeedbackMessage(QuicTime::Delta spike_time) {
+ QuicCongestionFeedbackFrame feedback;
+ feedback.type = kInterArrival;
+ feedback.inter_arrival.accumulated_number_of_lost_packets = 0;
+ receive_clock_.AdvanceTime(spike_time);
+ QuicTime receive_time = receive_clock_.ApproximateNow();
+ feedback.inter_arrival.received_packet_times.insert(
+ std::pair<QuicPacketSequenceNumber, QuicTime>(
+ feedback_sequence_number_, receive_time));
+ feedback_sequence_number_++;
+
+ // We need to send feedback for 2 packets since they where sent at the
+ // same time.
+ feedback.inter_arrival.received_packet_times.insert(
+ std::pair<QuicPacketSequenceNumber, QuicTime>(
+ feedback_sequence_number_, receive_time));
+ feedback_sequence_number_++;
+
+ sender_.OnIncomingQuicCongestionFeedbackFrame(
+ feedback, send_clock_.Now(), QuicBandwidth::Zero(), sent_packets_);
+ }
+
+ void SendFeedbackMessageNPackets(int n,
+ QuicTime::Delta delta_odd,
+ QuicTime::Delta delta_even) {
+ QuicCongestionFeedbackFrame feedback;
+ feedback.type = kInterArrival;
+ feedback.inter_arrival.accumulated_number_of_lost_packets = 0;
+ for (int i = 0; i < n; ++i) {
+ if (feedback_sequence_number_ % 2) {
+ receive_clock_.AdvanceTime(delta_even);
+ } else {
+ receive_clock_.AdvanceTime(delta_odd);
+ }
+ QuicTime receive_time = receive_clock_.ApproximateNow();
+ feedback.inter_arrival.received_packet_times.insert(
+ std::pair<QuicPacketSequenceNumber, QuicTime>(
+ feedback_sequence_number_, receive_time));
+ feedback_sequence_number_++;
+ }
+ sender_.OnIncomingQuicCongestionFeedbackFrame(feedback, send_clock_.Now(),
+ QuicBandwidth::Zero(), sent_packets_);
+ }
+
+ QuicTime::Delta SenderDeltaSinceStart() {
+ return send_clock_.ApproximateNow().Subtract(send_start_time_);
+ }
+
+ const QuicTime::Delta rtt_;
+ const QuicTime::Delta one_ms_;
+ const QuicTime::Delta one_s_;
+ const QuicTime::Delta nine_ms_;
+ MockClock send_clock_;
+ MockClock receive_clock_;
+ const QuicTime send_start_time_;
+ InterArrivalSender sender_;
+ QuicPacketSequenceNumber sequence_number_;
+ QuicPacketSequenceNumber acked_sequence_number_;
+ QuicPacketSequenceNumber feedback_sequence_number_;
+ SendAlgorithmInterface::SentPacketsMap sent_packets_;
+};
+
+TEST_F(InterArrivalSenderTest, ProbeFollowedByFullRampUpCycle) {
+ QuicCongestionFeedbackFrame feedback;
+ // At startup make sure we can send.
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+
+ // Send 5 bursts.
+ for (int i = 0; i < 4; ++i) {
+ SendAvailableCongestionWindow();
+ send_clock_.AdvanceTime(sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA));
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ }
+ SendAvailableCongestionWindow();
+
+ // We have now sent our probe.
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsInfinite());
+
+ AckNPackets(10);
+ SendFeedbackMessageNPackets(10, one_ms_, nine_ms_);
+ send_clock_.AdvanceTime(sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA));
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+
+ // We should now have our probe rate.
+ QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41);
+ int64 probe_rate = kMaxPacketSize * 9 * kNumMicrosPerSecond /
+ acc_arrival_time.ToMicroseconds();
+ EXPECT_NEAR(0.7f * probe_rate,
+ sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
+ DLOG(INFO) << "After probe";
+ // Send 50 bursts, make sure that we move fast in the beginning.
+ for (int i = 0; i < 50; ++i) {
+ SendAvailableCongestionWindow();
+ QuicTime::Delta time_until_send = sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ send_clock_.AdvanceTime(time_until_send);
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ AckNPackets(2);
+ SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
+ }
+ EXPECT_NEAR(0.875f * probe_rate,
+ sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
+ EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 600, 10);
+
+ // Send 50 bursts, make sure that we slow down towards the probe rate.
+ for (int i = 0; i < 50; ++i) {
+ SendAvailableCongestionWindow();
+ QuicTime::Delta time_until_send = sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ send_clock_.AdvanceTime(time_until_send);
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ AckNPackets(2);
+ SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
+ }
+ EXPECT_NEAR(0.95f * probe_rate,
+ sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
+ EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 1100, 10);
+
+ // Send 50 bursts, make sure that we move very slow close to the probe rate.
+ for (int i = 0; i < 50; ++i) {
+ SendAvailableCongestionWindow();
+ QuicTime::Delta time_until_send = sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ send_clock_.AdvanceTime(time_until_send);
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ AckNPackets(2);
+ SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
+ }
+ EXPECT_NEAR(0.99f * probe_rate,
+ sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
+ EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 1560, 10);
+ DLOG(INFO) << "Near available channel estimate";
+
+ // Send 50 bursts, make sure that we move very slow close to the probe rate.
+ for (int i = 0; i < 50; ++i) {
+ SendAvailableCongestionWindow();
+ QuicTime::Delta time_until_send = sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ send_clock_.AdvanceTime(time_until_send);
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ AckNPackets(2);
+ SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
+ }
+ EXPECT_NEAR(1.00f * probe_rate,
+ sender_.BandwidthEstimate().ToBytesPerSecond(), 2000);
+ EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 2000, 100);
+ DLOG(INFO) << "At available channel estimate";
+
+ // Send 50 bursts, make sure that we move very slow close to the probe rate.
+ for (int i = 0; i < 50; ++i) {
+ SendAvailableCongestionWindow();
+ QuicTime::Delta time_until_send = sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ send_clock_.AdvanceTime(time_until_send);
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ AckNPackets(2);
+ SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
+ }
+ EXPECT_NEAR(1.01f * probe_rate,
+ sender_.BandwidthEstimate().ToBytesPerSecond(), 2000);
+ EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 2500, 100);
+
+ // Send 50 bursts, make sure that we accelerate after the probe rate.
+ for (int i = 0; i < 50; ++i) {
+ SendAvailableCongestionWindow();
+ QuicTime::Delta time_until_send = sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ send_clock_.AdvanceTime(time_until_send);
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ AckNPackets(2);
+ SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
+ }
+ EXPECT_NEAR(1.01f * probe_rate,
+ sender_.BandwidthEstimate().ToBytesPerSecond(), 2000);
+ EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 2900, 100);
+
+ // Send 50 bursts, make sure that we accelerate after the probe rate.
+ for (int i = 0; i < 50; ++i) {
+ SendAvailableCongestionWindow();
+ QuicTime::Delta time_until_send = sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ send_clock_.AdvanceTime(time_until_send);
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ AckNPackets(2);
+ SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
+ }
+ EXPECT_NEAR(1.03f * probe_rate,
+ sender_.BandwidthEstimate().ToBytesPerSecond(), 2000);
+ EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 3400, 100);
+
+ int64 max_rate = kMaxPacketSize * kNumMicrosPerSecond /
+ one_ms_.ToMicroseconds();
+
+ int64 halfway_rate = probe_rate + (max_rate - probe_rate) / 2;
+
+ // Send until we reach halfway point.
+ for (int i = 0; i < 570; ++i) {
+ SendAvailableCongestionWindow();
+ QuicTime::Delta time_until_send = sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ send_clock_.AdvanceTime(time_until_send);
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ AckNPackets(2);
+ SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
+ }
+ EXPECT_NEAR(halfway_rate,
+ sender_.BandwidthEstimate().ToBytesPerSecond(), 5000);
+ EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 6600, 100);
+ DLOG(INFO) << "Near halfway point";
+
+ // Send until we reach max channel capacity.
+ for (int i = 0; i < 1500; ++i) {
+ SendAvailableCongestionWindow();
+ QuicTime::Delta time_until_send = sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ send_clock_.AdvanceTime(time_until_send);
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ AckNPackets(2);
+ SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
+ }
+ EXPECT_NEAR(max_rate,
+ sender_.BandwidthEstimate().ToBytesPerSecond(), 5000);
+ EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 10000, 200);
+}
+
+TEST_F(InterArrivalSenderTest, DelaySpikeFollowedBySlowDrain) {
+ QuicCongestionFeedbackFrame feedback;
+ // At startup make sure we can send.
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+
+ // Send 5 bursts.
+ for (int i = 0; i < 4; ++i) {
+ SendAvailableCongestionWindow();
+ send_clock_.AdvanceTime(sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA));
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ }
+ SendAvailableCongestionWindow();
+
+ // We have now sent our probe.
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsInfinite());
+
+ AckNPackets(10);
+ SendFeedbackMessageNPackets(10, one_ms_, nine_ms_);
+ send_clock_.AdvanceTime(sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA));
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+
+ // We should now have our probe rate.
+ QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41);
+ int64 probe_rate = kMaxPacketSize * 9 * kNumMicrosPerSecond /
+ acc_arrival_time.ToMicroseconds();
+ EXPECT_NEAR(0.7f * probe_rate,
+ sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
+
+ // Send 50 bursts, make sure that we move fast in the beginning.
+ for (int i = 0; i < 50; ++i) {
+ SendAvailableCongestionWindow();
+ QuicTime::Delta time_until_send = sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ send_clock_.AdvanceTime(time_until_send);
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ AckNPackets(2);
+ SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
+ }
+ EXPECT_NEAR(0.875f * probe_rate,
+ sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
+ EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 600, 10);
+
+ SendAvailableCongestionWindow();
+ send_clock_.AdvanceTime(sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA));
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ AckNPackets(2);
+
+ int64 rate_at_introduced_delay_spike = 0.875f * probe_rate;
+ QuicTime::Delta spike_time = QuicTime::Delta::FromMilliseconds(100);
+ SendDelaySpikeFeedbackMessage(spike_time);
+
+ // Backing as much as we can, currently 90%.
+ EXPECT_NEAR(0.1f * rate_at_introduced_delay_spike,
+ sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
+ EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 610, 10);
+
+ // Run until we are catched up after our introduced delay spike.
+ while (send_clock_.Now() < receive_clock_.Now()) {
+ SendAvailableCongestionWindow();
+ send_clock_.AdvanceTime(sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA));
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ AckNPackets(2);
+ SendFeedbackMessageNPackets(2, one_ms_, one_ms_);
+ }
+ // Expect that we go back to 67% of the rate before the spike.
+ EXPECT_NEAR(0.67f * rate_at_introduced_delay_spike,
+ sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
+ EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 820, 10);
+
+ // Send 100 bursts, make sure that we slow down towards the rate we had
+ // before the spike.
+ for (int i = 0; i < 100; ++i) {
+ SendAvailableCongestionWindow();
+ QuicTime::Delta time_until_send = sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ send_clock_.AdvanceTime(time_until_send);
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ AckNPackets(2);
+ SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
+ }
+ EXPECT_NEAR(0.97f * rate_at_introduced_delay_spike,
+ sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
+ EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 2100, 10);
+}
+
+TEST_F(InterArrivalSenderTest, DelaySpikeFollowedByImmediateDrain) {
+ QuicCongestionFeedbackFrame feedback;
+ // At startup make sure we can send.
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+
+ // Send 5 bursts.
+ for (int i = 0; i < 4; ++i) {
+ SendAvailableCongestionWindow();
+ send_clock_.AdvanceTime(sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA));
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ }
+ SendAvailableCongestionWindow();
+
+ // We have now sent our probe.
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsInfinite());
+
+ AckNPackets(10);
+ SendFeedbackMessageNPackets(10, one_ms_, nine_ms_);
+ send_clock_.AdvanceTime(sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA));
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+
+ // We should now have our probe rate.
+ QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41);
+ int64 probe_rate = kMaxPacketSize * 9 * kNumMicrosPerSecond /
+ acc_arrival_time.ToMicroseconds();
+ EXPECT_NEAR(0.7f * probe_rate,
+ sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
+
+ // Send 50 bursts, make sure that we move fast in the beginning.
+ for (int i = 0; i < 50; ++i) {
+ SendAvailableCongestionWindow();
+ QuicTime::Delta time_until_send = sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ send_clock_.AdvanceTime(time_until_send);
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ AckNPackets(2);
+ SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_));
+ }
+ EXPECT_NEAR(0.875f * probe_rate,
+ sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
+ EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 600, 10);
+
+ SendAvailableCongestionWindow();
+ send_clock_.AdvanceTime(sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA));
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ AckNPackets(2);
+
+ int64 rate_at_introduced_delay_spike = 0.875f * probe_rate;
+ QuicTime::Delta spike_time = QuicTime::Delta::FromMilliseconds(100);
+ SendDelaySpikeFeedbackMessage(spike_time);
+
+ // Backing as much as we can, currently 90%.
+ EXPECT_NEAR(0.1f * rate_at_introduced_delay_spike,
+ sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
+ EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 610, 10);
+
+ // Move send time forward.
+ send_clock_.AdvanceTime(spike_time);
+ // Make sure our clocks are aligned again.
+ receive_clock_.AdvanceTime(send_clock_.Now().Subtract(receive_clock_.Now()));
+
+ SendAvailableCongestionWindow();
+ AckNPackets(2);
+ SendFeedbackMessageNPackets(2, one_ms_, one_ms_);
+ // We should now be back where we introduced the delay spike.
+ EXPECT_NEAR(rate_at_introduced_delay_spike,
+ sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
+ EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 710, 10);
+}
+
+TEST_F(InterArrivalSenderTest, MinBitrateDueToDelay) {
+ QuicBandwidth expected_min_bitrate = QuicBandwidth::FromKBitsPerSecond(10);
+ QuicCongestionFeedbackFrame feedback;
+ // At startup make sure we can send.
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+
+ // Send 5 bursts.
+ for (int i = 0; i < 4; ++i) {
+ SendAvailableCongestionWindow();
+ send_clock_.AdvanceTime(sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA));
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ }
+ SendAvailableCongestionWindow();
+
+ AckNPackets(10);
+
+ // One second spread per packet is expected to result in an estimate at
+ // our minimum bitrate.
+ SendFeedbackMessageNPackets(10, one_s_, one_s_);
+ send_clock_.AdvanceTime(sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA));
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ EXPECT_EQ(expected_min_bitrate, sender_.BandwidthEstimate());
+}
+
+TEST_F(InterArrivalSenderTest, MinBitrateDueToLoss) {
+ QuicBandwidth expected_min_bitrate = QuicBandwidth::FromKBitsPerSecond(10);
+ QuicCongestionFeedbackFrame feedback;
+ // At startup make sure we can send.
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+
+ // Send 5 bursts.
+ for (int i = 0; i < 4; ++i) {
+ SendAvailableCongestionWindow();
+ send_clock_.AdvanceTime(sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA));
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ }
+ SendAvailableCongestionWindow();
+
+ AckNPackets(10);
+ SendFeedbackMessageNPackets(10, nine_ms_, nine_ms_);
+ send_clock_.AdvanceTime(sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA));
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+
+ QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(81);
+ int64 probe_rate = kMaxPacketSize * 9 * kNumMicrosPerSecond /
+ acc_arrival_time.ToMicroseconds();
+ EXPECT_NEAR(0.7f * probe_rate,
+ sender_.BandwidthEstimate().ToBytesPerSecond(), 1000);
+
+ for (int i = 0; i < 15; ++i) {
+ SendAvailableCongestionWindow();
+ QuicTime::Delta time_until_send = sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ send_clock_.AdvanceTime(time_until_send);
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ sender_.OnIncomingLoss(send_clock_.Now());
+ sender_.OnIncomingAck(acked_sequence_number_, kMaxPacketSize, rtt_);
+ acked_sequence_number_ += 2; // Create a loss by not acking both packets.
+ SendFeedbackMessageNPackets(2, nine_ms_, nine_ms_);
+ }
+ // Test that our exponentail back off stop at expected_min_bitrate.
+ EXPECT_EQ(expected_min_bitrate, sender_.BandwidthEstimate());
+
+ for (int i = 0; i < 50; ++i) {
+ SendAvailableCongestionWindow();
+ QuicTime::Delta time_until_send = sender_.TimeUntilSend(
+ send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ send_clock_.AdvanceTime(time_until_send);
+ EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ sender_.OnIncomingLoss(send_clock_.Now());
+ sender_.OnIncomingAck(acked_sequence_number_, kMaxPacketSize, rtt_);
+ acked_sequence_number_ += 2; // Create a loss by not acking both packets.
+ SendFeedbackMessageNPackets(2, nine_ms_, nine_ms_);
+
+ // Make sure our bitrate is fixed at the expected_min_bitrate.
+ EXPECT_EQ(expected_min_bitrate, sender_.BandwidthEstimate());
+ }
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/congestion_control/inter_arrival_state_machine.cc b/net/quic/congestion_control/inter_arrival_state_machine.cc
new file mode 100644
index 0000000..7095e603
--- /dev/null
+++ b/net/quic/congestion_control/inter_arrival_state_machine.cc
@@ -0,0 +1,163 @@
+// 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 "net/quic/congestion_control/inter_arrival_state_machine.h"
+
+#include "base/logging.h"
+
+namespace {
+const int kIncreaseEventsBeforeDowngradingState = 5;
+const int kDecreaseEventsBeforeUpgradingState = 2;
+// Note: Can not be higher than kDecreaseEventsBeforeUpgradingState;
+const int kLossEventsBeforeUpgradingState = 2;
+// Timeout old loss and delay events after this time.
+const int kEventTimeoutMs = 10000;
+// A reasonable arbitrary chosen value for initial round trip time.
+const int kInitialRttMs = 80;
+}
+
+namespace net {
+
+InterArrivalStateMachine::InterArrivalStateMachine(const QuicClock* clock)
+ : clock_(clock),
+ current_state_(kInterArrivalStateStable),
+ smoothed_rtt_(QuicTime::Delta::FromMilliseconds(kInitialRttMs)),
+ decrease_event_count_(0),
+ last_decrease_event_(QuicTime::Zero()),
+ increase_event_count_(0),
+ last_increase_event_(QuicTime::Zero()),
+ loss_event_count_(0),
+ last_loss_event_(QuicTime::Zero()),
+ delay_event_count_(0),
+ last_delay_event_(QuicTime::Zero()) {
+}
+
+InterArrivalState InterArrivalStateMachine::GetInterArrivalState() {
+ return current_state_;
+}
+
+void InterArrivalStateMachine::IncreaseBitrateDecision() {
+ // Multiple increase event without packet loss or delay events will drive
+ // state back to stable.
+ QuicTime current_time = clock_->ApproximateNow();
+ if (current_time.Subtract(last_increase_event_) < smoothed_rtt_) {
+ // Less than one RTT have passed; ignore this event.
+ return;
+ }
+ last_increase_event_ = current_time;
+ increase_event_count_++;
+ decrease_event_count_ = 0; // Reset previous decrease events.
+
+ if (increase_event_count_ < kIncreaseEventsBeforeDowngradingState) {
+ // Not enough increase events to change state.
+ return;
+ }
+ increase_event_count_ = 0; // Reset increase events.
+
+ switch (current_state_) {
+ case kInterArrivalStateStable:
+ // Keep this state.
+ break;
+ case kInterArrivalStatePacketLoss:
+ current_state_ = kInterArrivalStateStable;
+ break;
+ case kInterArrivalStateDelay:
+ current_state_ = kInterArrivalStateStable;
+ break;
+ case kInterArrivalStateCompetingFlow:
+ current_state_ = kInterArrivalStateDelay;
+ break;
+ case kInterArrivalStateCompetingTcpFLow:
+ current_state_ = kInterArrivalStateDelay;
+ break;
+ }
+}
+
+void InterArrivalStateMachine::DecreaseBitrateDecision() {
+ DCHECK(kDecreaseEventsBeforeUpgradingState >=
+ kLossEventsBeforeUpgradingState);
+
+ QuicTime current_time = clock_->ApproximateNow();
+ if (current_time.Subtract(last_decrease_event_) < smoothed_rtt_) {
+ // Less than one RTT have passed; ignore this event.
+ return;
+ }
+ last_decrease_event_ = current_time;
+ decrease_event_count_++;
+ increase_event_count_ = 0; // Reset previous increase events.
+ if (decrease_event_count_ < kDecreaseEventsBeforeUpgradingState) {
+ // Not enough decrease events to change state.
+ return;
+ }
+ decrease_event_count_ = 0; // Reset decrease events.
+
+ switch (current_state_) {
+ case kInterArrivalStateStable:
+ if (delay_event_count_ == 0 && loss_event_count_ > 0) {
+ // No recent delay events; only packet loss events.
+ current_state_ = kInterArrivalStatePacketLoss;
+ } else {
+ current_state_ = kInterArrivalStateDelay;
+ }
+ break;
+ case kInterArrivalStatePacketLoss:
+ // Keep this state.
+ break;
+ case kInterArrivalStateDelay:
+ if (loss_event_count_ >= kLossEventsBeforeUpgradingState) {
+ // We have packet loss events. Assume fighting with TCP.
+ current_state_ = kInterArrivalStateCompetingTcpFLow;
+ } else {
+ current_state_ = kInterArrivalStateCompetingFlow;
+ }
+ break;
+ case kInterArrivalStateCompetingFlow:
+ if (loss_event_count_ >= kLossEventsBeforeUpgradingState) {
+ // We have packet loss events. Assume fighting with TCP.
+ current_state_ = kInterArrivalStateCompetingTcpFLow;
+ }
+ break;
+ case kInterArrivalStateCompetingTcpFLow:
+ // Keep this state.
+ break;
+ }
+}
+
+void InterArrivalStateMachine::set_rtt(QuicTime::Delta rtt) {
+ smoothed_rtt_ = rtt;
+}
+
+bool InterArrivalStateMachine::PacketLossEvent() {
+ QuicTime current_time = clock_->ApproximateNow();
+ if (current_time.Subtract(last_loss_event_) < smoothed_rtt_) {
+ // Less than one RTT have passed; ignore this event.
+ return false;
+ }
+ last_loss_event_ = current_time;
+ loss_event_count_++;
+ if (current_time.Subtract(last_delay_event_) >
+ QuicTime::Delta::FromMilliseconds(kEventTimeoutMs)) {
+ // Delay event have timed out.
+ delay_event_count_ = 0;
+ }
+ return true;
+}
+
+bool InterArrivalStateMachine::IncreasingDelayEvent() {
+ QuicTime current_time = clock_->ApproximateNow();
+ if (current_time.Subtract(last_delay_event_) < smoothed_rtt_) {
+ // Less than one RTT have passed; ignore this event.
+ return false;
+ }
+ last_delay_event_ = current_time;
+ delay_event_count_++;
+ if (current_time.Subtract(last_loss_event_) >
+ QuicTime::Delta::FromMilliseconds(kEventTimeoutMs)) {
+ // Loss event have timed out.
+ loss_event_count_ = 0;
+ }
+ return true;
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/inter_arrival_state_machine.h b/net/quic/congestion_control/inter_arrival_state_machine.h
new file mode 100644
index 0000000..829aa29
--- /dev/null
+++ b/net/quic/congestion_control/inter_arrival_state_machine.h
@@ -0,0 +1,94 @@
+// 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.
+
+// State machine for congestion control. The state is updated by calls from
+// other modules as they detect events, or decide on taking specific actions.
+// Events include things like packet loss, or growing delay, while decisions
+// include decisions to increase or decrease bitrates.
+// This class should be called for every event and decision made by the
+// congestion control, this class will coalesce all calls relative to the
+// smoothed RTT.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_STATE_MACHINE_H_
+#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_STATE_MACHINE_H_
+
+#include "net/base/net_export.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+// State transition diagram.
+//
+// kInterArrivalStatePacketLoss
+// |
+// kInterArrivalStateStable
+// |
+// kInterArrivalStateDelay
+// | |
+// kInterArrivalStateCompetingFlow -> kInterArrivalStateCompetingTcpFLow
+
+enum NET_EXPORT_PRIVATE InterArrivalState {
+ // We are on a network with a delay that is too small to be reliably detected,
+ // such as a local ethernet.
+ kInterArrivalStatePacketLoss = 1,
+ // We are on an underutilized network operating with low delay and low loss.
+ kInterArrivalStateStable = 2,
+ // We are on a network where we can detect delay changes and suffer only
+ // low loss. Nothing indicates that we are competing with another flow.
+ kInterArrivalStateDelay = 3,
+ // We are on a network where we can detect delay changes and suffer only
+ // low loss. We have indications that we are compete with another flow.
+ kInterArrivalStateCompetingFlow = 4,
+ // We are on a network where we can detect delay changes, however we suffer
+ // packet loss due to sharing the bottleneck link with another flow, which is
+ // most likely a TCP flow.
+ kInterArrivalStateCompetingTcpFLow = 5,
+};
+
+class NET_EXPORT_PRIVATE InterArrivalStateMachine {
+ public:
+ explicit InterArrivalStateMachine(const QuicClock* clock);
+
+ InterArrivalState GetInterArrivalState();
+
+ // Inter arrival congestion control decided to increase bitrate.
+ void IncreaseBitrateDecision();
+
+ // Inter arrival congestion control decided to decrease bitrate.
+ void DecreaseBitrateDecision();
+
+ // Estimated smoothed round trip time.
+ // This should be called whenever the smoothed RTT estimate is updated.
+ void set_rtt(QuicTime::Delta rtt);
+
+ // This method is called when a packet loss was reported.
+ bool PacketLossEvent();
+
+ // This method is called when we believe that packet transit delay is
+ // increasing, presumably due to a growing queue along the path.
+ bool IncreasingDelayEvent();
+
+ private:
+ const QuicClock* clock_;
+ InterArrivalState current_state_;
+ QuicTime::Delta smoothed_rtt_;
+
+ int decrease_event_count_;
+ QuicTime last_decrease_event_;
+
+ int increase_event_count_;
+ QuicTime last_increase_event_;
+
+ int loss_event_count_;
+ QuicTime last_loss_event_;
+
+ int delay_event_count_;
+ QuicTime last_delay_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterArrivalStateMachine);
+};
+
+} // namespace net
+#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_STATE_MACHINE_H_
diff --git a/net/quic/congestion_control/inter_arrival_state_machine_test.cc b/net/quic/congestion_control/inter_arrival_state_machine_test.cc
new file mode 100644
index 0000000..10759c22d
--- /dev/null
+++ b/net/quic/congestion_control/inter_arrival_state_machine_test.cc
@@ -0,0 +1,126 @@
+// 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 "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/congestion_control/inter_arrival_state_machine.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class InterArrivalStateMachineTest : public ::testing::Test {
+ protected:
+ InterArrivalStateMachineTest() {
+ }
+
+ void SetUp() {
+ state_machine_.reset(new InterArrivalStateMachine(&clock_));
+ }
+
+ MockClock clock_;
+ scoped_ptr<InterArrivalStateMachine> state_machine_;
+};
+
+TEST_F(InterArrivalStateMachineTest, SimplePacketLoss) {
+ QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(100);
+ state_machine_->set_rtt(rtt);
+ state_machine_->IncreaseBitrateDecision();
+
+ clock_.AdvanceTime(rtt);
+ state_machine_->PacketLossEvent();
+ state_machine_->DecreaseBitrateDecision();
+ EXPECT_EQ(kInterArrivalStateStable,
+ state_machine_->GetInterArrivalState());
+
+ // Make sure we switch to state packet loss.
+ clock_.AdvanceTime(rtt);
+ state_machine_->PacketLossEvent();
+ state_machine_->DecreaseBitrateDecision();
+ EXPECT_EQ(kInterArrivalStatePacketLoss,
+ state_machine_->GetInterArrivalState());
+
+ // Make sure we stay in state packet loss.
+ clock_.AdvanceTime(rtt);
+ state_machine_->PacketLossEvent();
+ state_machine_->DecreaseBitrateDecision();
+ EXPECT_EQ(kInterArrivalStatePacketLoss,
+ state_machine_->GetInterArrivalState());
+
+ clock_.AdvanceTime(rtt);
+ state_machine_->PacketLossEvent();
+ state_machine_->DecreaseBitrateDecision();
+ EXPECT_EQ(kInterArrivalStatePacketLoss,
+ state_machine_->GetInterArrivalState());
+}
+
+TEST_F(InterArrivalStateMachineTest, SimpleDelay) {
+ QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(100);
+ state_machine_->set_rtt(rtt);
+ state_machine_->IncreaseBitrateDecision();
+
+ clock_.AdvanceTime(rtt);
+ state_machine_->IncreasingDelayEvent();
+ state_machine_->DecreaseBitrateDecision();
+ EXPECT_EQ(kInterArrivalStateStable,
+ state_machine_->GetInterArrivalState());
+
+ // Make sure we switch to state delay.
+ clock_.AdvanceTime(rtt);
+ state_machine_->IncreasingDelayEvent();
+ state_machine_->DecreaseBitrateDecision();
+ EXPECT_EQ(kInterArrivalStateDelay,
+ state_machine_->GetInterArrivalState());
+
+ clock_.AdvanceTime(rtt);
+ state_machine_->IncreasingDelayEvent();
+ state_machine_->DecreaseBitrateDecision();
+ EXPECT_EQ(kInterArrivalStateDelay,
+ state_machine_->GetInterArrivalState());
+
+ // Make sure we switch to state competing flow(s).
+ clock_.AdvanceTime(rtt);
+ state_machine_->IncreasingDelayEvent();
+ state_machine_->DecreaseBitrateDecision();
+ EXPECT_EQ(kInterArrivalStateCompetingFlow,
+ state_machine_->GetInterArrivalState());
+
+ // Make sure we stay in state competing flow(s).
+ clock_.AdvanceTime(rtt);
+ state_machine_->IncreasingDelayEvent();
+ state_machine_->DecreaseBitrateDecision();
+ EXPECT_EQ(kInterArrivalStateCompetingFlow,
+ state_machine_->GetInterArrivalState());
+
+ clock_.AdvanceTime(rtt);
+ state_machine_->PacketLossEvent();
+ state_machine_->DecreaseBitrateDecision();
+ EXPECT_EQ(kInterArrivalStateCompetingFlow,
+ state_machine_->GetInterArrivalState());
+
+ clock_.AdvanceTime(rtt);
+ state_machine_->PacketLossEvent();
+ state_machine_->DecreaseBitrateDecision();
+ EXPECT_EQ(kInterArrivalStateCompetingFlow,
+ state_machine_->GetInterArrivalState());
+
+ // Make sure we switch to state competing TCP flow(s).
+ clock_.AdvanceTime(rtt);
+ state_machine_->PacketLossEvent();
+ state_machine_->DecreaseBitrateDecision();
+ EXPECT_EQ(kInterArrivalStateCompetingTcpFLow,
+ state_machine_->GetInterArrivalState());
+
+ // Make sure we stay in state competing TCP flow(s).
+ clock_.AdvanceTime(rtt);
+ state_machine_->PacketLossEvent();
+ state_machine_->DecreaseBitrateDecision();
+ EXPECT_EQ(kInterArrivalStateCompetingTcpFLow,
+ state_machine_->GetInterArrivalState());
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/congestion_control/quic_congestion_control_test.cc b/net/quic/congestion_control/quic_congestion_control_test.cc
new file mode 100644
index 0000000..0a581d0
--- /dev/null
+++ b/net/quic/congestion_control/quic_congestion_control_test.cc
@@ -0,0 +1,138 @@
+// 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.
+
+// Test of the full congestion control chain.
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/congestion_control/quic_congestion_manager.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::max;
+
+namespace net {
+namespace test {
+
+class QuicCongestionManagerPeer : public QuicCongestionManager {
+ public:
+ explicit QuicCongestionManagerPeer(const QuicClock* clock,
+ CongestionFeedbackType congestion_type)
+ : QuicCongestionManager(clock, congestion_type) {
+ }
+ using QuicCongestionManager::SentBandwidth;
+ using QuicCongestionManager::BandwidthEstimate;
+};
+
+class QuicCongestionControlTest : public ::testing::Test {
+ protected:
+ QuicCongestionControlTest()
+ : start_(clock_.ApproximateNow()) {
+ }
+
+ void SetUpCongestionType(CongestionFeedbackType congestion_type) {
+ manager_.reset(new QuicCongestionManagerPeer(&clock_, congestion_type));
+ }
+
+ MockClock clock_;
+ QuicTime start_;
+ scoped_ptr<QuicCongestionManagerPeer> manager_;
+};
+
+TEST_F(QuicCongestionControlTest, FixedRateSenderAPI) {
+ SetUpCongestionType(kFixRate);
+ QuicCongestionFeedbackFrame congestion_feedback;
+ congestion_feedback.type = kFixRate;
+ congestion_feedback.fix_rate.bitrate = QuicBandwidth::FromKBytesPerSecond(30);
+ manager_->OnIncomingQuicCongestionFeedbackFrame(congestion_feedback,
+ clock_.Now());
+ EXPECT_TRUE(manager_->SentBandwidth(clock_.Now()).IsZero());
+ EXPECT_TRUE(manager_->TimeUntilSend(
+ clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA).IsZero());
+ manager_->SentPacket(1, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION);
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(40),
+ manager_->TimeUntilSend(
+ clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA));
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(35));
+ EXPECT_EQ(QuicTime::Delta::Infinite(),
+ manager_->TimeUntilSend(
+ clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA));
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_EQ(QuicTime::Delta::Infinite(),
+ manager_->TimeUntilSend(
+ clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA));
+}
+
+TEST_F(QuicCongestionControlTest, FixedRatePacing) {
+ SetUpCongestionType(kFixRate);
+ QuicAckFrame ack;
+ ack.received_info.largest_observed = 0;
+ manager_->OnIncomingAckFrame(ack, clock_.Now());
+
+ QuicCongestionFeedbackFrame feedback;
+ feedback.type = kFixRate;
+ feedback.fix_rate.bitrate = QuicBandwidth::FromKBytesPerSecond(100);
+ manager_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
+
+ QuicTime acc_advance_time(QuicTime::Zero());
+ for (QuicPacketSequenceNumber i = 1; i <= 100; ++i) {
+ EXPECT_TRUE(manager_->TimeUntilSend(
+ clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA).IsZero());
+ manager_->SentPacket(i, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION);
+ QuicTime::Delta advance_time = manager_->TimeUntilSend(
+ clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ clock_.AdvanceTime(advance_time);
+ acc_advance_time = acc_advance_time.Add(advance_time);
+ // Ack the packet we sent.
+ ack.received_info.largest_observed = max(
+ i, ack.received_info.largest_observed);
+ manager_->OnIncomingAckFrame(ack, clock_.Now());
+ }
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(1200),
+ acc_advance_time.Subtract(start_));
+}
+
+TEST_F(QuicCongestionControlTest, Pacing) {
+ SetUpCongestionType(kFixRate);
+ QuicAckFrame ack;
+ ack.received_info.largest_observed = 0;
+ manager_->OnIncomingAckFrame(ack, clock_.Now());
+
+ QuicCongestionFeedbackFrame feedback;
+ feedback.type = kFixRate;
+ // Test a high bitrate (8Mbit/s) to trigger pacing.
+ feedback.fix_rate.bitrate = QuicBandwidth::FromKBytesPerSecond(1000);
+ manager_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
+
+ QuicTime acc_advance_time(QuicTime::Zero());
+ for (QuicPacketSequenceNumber i = 1; i <= 100;) {
+ EXPECT_TRUE(manager_->TimeUntilSend(
+ clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA).IsZero());
+ manager_->SentPacket(i++, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION);
+ EXPECT_TRUE(manager_->TimeUntilSend(
+ clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA).IsZero());
+ manager_->SentPacket(i++, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION);
+ QuicTime::Delta advance_time = manager_->TimeUntilSend(
+ clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ clock_.AdvanceTime(advance_time);
+ acc_advance_time = acc_advance_time.Add(advance_time);
+ // Ack the packets we sent.
+ ack.received_info.largest_observed = max(
+ i - 2, ack.received_info.largest_observed);
+ manager_->OnIncomingAckFrame(ack, clock_.Now());
+ ack.received_info.largest_observed = max(
+ i - 1, ack.received_info.largest_observed);
+ manager_->OnIncomingAckFrame(ack, clock_.Now());
+ }
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(120),
+ acc_advance_time.Subtract(start_));
+}
+
+// TODO(pwestin): add TCP tests.
+
+// TODO(pwestin): add InterArrival tests.
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/congestion_control/quic_congestion_manager.h b/net/quic/congestion_control/quic_congestion_manager.h
index 5840444..1ffe12a5 100644
--- a/net/quic/congestion_control/quic_congestion_manager.h
+++ b/net/quic/congestion_control/quic_congestion_manager.h
@@ -1,3 +1,7 @@
+// 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.
+
// 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.
@@ -26,7 +30,7 @@
class QuicClock;
class ReceiveAlgorithmInterface;
-class QuicCongestionManager {
+class NET_EXPORT_PRIVATE QuicCongestionManager {
public:
QuicCongestionManager(const QuicClock* clock,
CongestionFeedbackType congestion_type);
diff --git a/net/quic/congestion_control/quic_congestion_manager_test.cc b/net/quic/congestion_control/quic_congestion_manager_test.cc
new file mode 100644
index 0000000..fb6d2e8
--- /dev/null
+++ b/net/quic/congestion_control/quic_congestion_manager_test.cc
@@ -0,0 +1,217 @@
+// 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 "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/congestion_control/quic_congestion_manager.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::StrictMock;
+
+namespace net {
+namespace test {
+
+class QuicCongestionManagerPeer : public QuicCongestionManager {
+ public:
+ explicit QuicCongestionManagerPeer(const QuicClock* clock,
+ CongestionFeedbackType congestion_type)
+ : QuicCongestionManager(clock, congestion_type) {
+ }
+ void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm) {
+ this->send_algorithm_.reset(send_algorithm);
+ }
+
+ using QuicCongestionManager::rtt;
+ using QuicCongestionManager::SentBandwidth;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicCongestionManagerPeer);
+};
+
+class QuicCongestionManagerTest : public ::testing::Test {
+ protected:
+ void SetUpCongestionType(CongestionFeedbackType congestion_type) {
+ manager_.reset(new QuicCongestionManagerPeer(&clock_, congestion_type));
+ }
+ MockClock clock_;
+ scoped_ptr<QuicCongestionManagerPeer> manager_;
+};
+
+TEST_F(QuicCongestionManagerTest, Bandwidth) {
+ SetUpCongestionType(kFixRate);
+ QuicAckFrame ack;
+ manager_->OnIncomingAckFrame(ack, clock_.Now());
+
+ QuicCongestionFeedbackFrame feedback;
+ feedback.type = kFixRate;
+ feedback.fix_rate.bitrate = QuicBandwidth::FromKBytesPerSecond(100);
+ manager_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
+
+ for (int i = 1; i <= 100; ++i) {
+ QuicTime::Delta advance_time = manager_->TimeUntilSend(
+ clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ clock_.AdvanceTime(advance_time);
+ EXPECT_TRUE(manager_->TimeUntilSend(
+ clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA).IsZero());
+ manager_->SentPacket(i, clock_.Now(), 1000, NOT_RETRANSMISSION);
+ // Ack the packet we sent.
+ ack.received_info.largest_observed = i;
+ manager_->OnIncomingAckFrame(ack, clock_.Now());
+ }
+ EXPECT_EQ(100, manager_->BandwidthEstimate().ToKBytesPerSecond());
+ EXPECT_NEAR(100,
+ manager_->SentBandwidth(clock_.Now()).ToKBytesPerSecond(), 4);
+}
+
+TEST_F(QuicCongestionManagerTest, BandwidthWith1SecondGap) {
+ SetUpCongestionType(kFixRate);
+ QuicAckFrame ack;
+ manager_->OnIncomingAckFrame(ack, clock_.Now());
+
+ QuicCongestionFeedbackFrame feedback;
+ feedback.type = kFixRate;
+ feedback.fix_rate.bitrate = QuicBandwidth::FromKBytesPerSecond(100);
+ manager_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now());
+
+ for (QuicPacketSequenceNumber sequence_number = 1; sequence_number <= 100;
+ ++sequence_number) {
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+ EXPECT_TRUE(manager_->TimeUntilSend(
+ clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA).IsZero());
+ manager_->SentPacket(
+ sequence_number, clock_.Now(), 1000, NOT_RETRANSMISSION);
+ // Ack the packet we sent.
+ ack.received_info.largest_observed = sequence_number;
+ manager_->OnIncomingAckFrame(ack, clock_.Now());
+ }
+ EXPECT_EQ(100000, manager_->BandwidthEstimate().ToBytesPerSecond());
+ EXPECT_NEAR(100000,
+ manager_->SentBandwidth(clock_.Now()).ToBytesPerSecond(), 2000);
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(500));
+ EXPECT_NEAR(50000,
+ manager_->SentBandwidth(clock_.Now()).ToBytesPerSecond(), 1000);
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(501));
+ EXPECT_NEAR(100000, manager_->BandwidthEstimate().ToBytesPerSecond(), 2000);
+ EXPECT_TRUE(manager_->SentBandwidth(clock_.Now()).IsZero());
+ for (int i = 1; i <= 150; ++i) {
+ EXPECT_TRUE(manager_->TimeUntilSend(
+ clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA).IsZero());
+ manager_->SentPacket(i + 100, clock_.Now(), 1000, NOT_RETRANSMISSION);
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+ // Ack the packet we sent.
+ ack.received_info.largest_observed = i + 100;
+ manager_->OnIncomingAckFrame(ack, clock_.Now());
+ }
+ EXPECT_EQ(100, manager_->BandwidthEstimate().ToKBytesPerSecond());
+ EXPECT_NEAR(100,
+ manager_->SentBandwidth(clock_.Now()).ToKBytesPerSecond(), 2);
+}
+
+TEST_F(QuicCongestionManagerTest, Rtt) {
+ SetUpCongestionType(kFixRate);
+
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ manager_->SetSendAlgorithm(send_algorithm);
+
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(15);
+
+ EXPECT_CALL(*send_algorithm, SentPacket(_, _, _, _)).Times(1);
+ EXPECT_CALL(*send_algorithm,
+ OnIncomingAck(sequence_number, _, expected_rtt)).Times(1);
+
+ manager_->SentPacket(sequence_number, clock_.Now(), 1000, NOT_RETRANSMISSION);
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
+
+ QuicAckFrame ack;
+ ack.received_info.largest_observed = sequence_number;
+ ack.received_info.delta_time_largest_observed =
+ QuicTime::Delta::FromMilliseconds(5);
+ manager_->OnIncomingAckFrame(ack, clock_.Now());
+ EXPECT_EQ(manager_->rtt(), expected_rtt);
+}
+
+TEST_F(QuicCongestionManagerTest, RttWithInvalidDelta) {
+ // Expect that the RTT is infinite since the delta_time_largest_observed is
+ // larger than the local time elapsed aka invalid.
+ SetUpCongestionType(kFixRate);
+
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ manager_->SetSendAlgorithm(send_algorithm);
+
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicTime::Delta expected_rtt = QuicTime::Delta::Infinite();
+
+ EXPECT_CALL(*send_algorithm, SentPacket(_, _, _, _)).Times(1);
+ EXPECT_CALL(*send_algorithm,
+ OnIncomingAck(sequence_number, _, expected_rtt)).Times(1);
+
+ manager_->SentPacket(sequence_number, clock_.Now(), 1000, NOT_RETRANSMISSION);
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+
+ QuicAckFrame ack;
+ ack.received_info.largest_observed = sequence_number;
+ ack.received_info.delta_time_largest_observed =
+ QuicTime::Delta::FromMilliseconds(11);
+ manager_->OnIncomingAckFrame(ack, clock_.Now());
+ EXPECT_EQ(manager_->rtt(), expected_rtt);
+}
+
+TEST_F(QuicCongestionManagerTest, RttInfiniteDelta) {
+ // Expect that the RTT is infinite since the delta_time_largest_observed is
+ // infinite aka invalid.
+ SetUpCongestionType(kFixRate);
+
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ manager_->SetSendAlgorithm(send_algorithm);
+
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicTime::Delta expected_rtt = QuicTime::Delta::Infinite();
+
+ EXPECT_CALL(*send_algorithm, SentPacket(_, _, _, _)).Times(1);
+ EXPECT_CALL(*send_algorithm,
+ OnIncomingAck(sequence_number, _, expected_rtt)).Times(1);
+
+ manager_->SentPacket(sequence_number, clock_.Now(), 1000, NOT_RETRANSMISSION);
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+
+ QuicAckFrame ack;
+ ack.received_info.largest_observed = sequence_number;
+ ack.received_info.delta_time_largest_observed = QuicTime::Delta::Infinite();
+ manager_->OnIncomingAckFrame(ack, clock_.Now());
+ EXPECT_EQ(manager_->rtt(), expected_rtt);
+}
+
+TEST_F(QuicCongestionManagerTest, RttZeroDelta) {
+ // Expect that the RTT is the time between send and receive since the
+ // delta_time_largest_observed is zero.
+ SetUpCongestionType(kFixRate);
+
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ manager_->SetSendAlgorithm(send_algorithm);
+
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
+
+ EXPECT_CALL(*send_algorithm, SentPacket(_, _, _, _)).Times(1);
+ EXPECT_CALL(*send_algorithm,
+ OnIncomingAck(sequence_number, _, expected_rtt)).Times(1);
+
+ manager_->SentPacket(sequence_number, clock_.Now(), 1000, NOT_RETRANSMISSION);
+ clock_.AdvanceTime(expected_rtt);
+
+ QuicAckFrame ack;
+ ack.received_info.largest_observed = sequence_number;
+ ack.received_info.delta_time_largest_observed = QuicTime::Delta::Zero();
+ manager_->OnIncomingAckFrame(ack, clock_.Now());
+ EXPECT_EQ(manager_->rtt(), expected_rtt);
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/congestion_control/quic_max_sized_map.h b/net/quic/congestion_control/quic_max_sized_map.h
new file mode 100644
index 0000000..a4ed776
--- /dev/null
+++ b/net/quic/congestion_control/quic_max_sized_map.h
@@ -0,0 +1,77 @@
+// 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.
+
+// Simple max sized map. Automatically deletes the oldest element when the
+// max limit is reached.
+// Note: the ConstIterator will NOT be valid after an Insert or RemoveAll.
+#ifndef NET_QUIC_CONGESTION_CONTROL_QUIC_MAX_SIZED_MAP_H_
+#define NET_QUIC_CONGESTION_CONTROL_QUIC_MAX_SIZED_MAP_H_
+
+#include <stdlib.h>
+
+#include <list>
+#include <map>
+
+#include "base/basictypes.h"
+
+namespace net {
+
+template <class Key, class Value>
+class QuicMaxSizedMap {
+ public:
+ typedef typename std::multimap<Key, Value>::const_iterator ConstIterator;
+
+ explicit QuicMaxSizedMap(size_t max_numer_of_items)
+ : max_numer_of_items_(max_numer_of_items) {
+ }
+
+ size_t MaxSize() const {
+ return max_numer_of_items_;
+ }
+
+ size_t Size() const {
+ return table_.size();
+ }
+
+ void Insert(const Key& k, const Value& value) {
+ if (Size() == MaxSize()) {
+ ListIterator list_it = insert_order_.begin();
+ table_.erase(*list_it);
+ insert_order_.pop_front();
+ }
+ TableIterator it = table_.insert(std::pair<Key, Value>(k, value));
+ insert_order_.push_back(it);
+ }
+
+ void RemoveAll() {
+ table_.clear();
+ insert_order_.clear();
+ }
+
+ // STL style const_iterator support.
+ ConstIterator Find(const Key& k) const {
+ return table_.find(k);
+ }
+
+ ConstIterator Begin() const {
+ return ConstIterator(table_.begin());
+ }
+
+ ConstIterator End() const {
+ return ConstIterator(table_.end());
+ }
+
+ private:
+ typedef typename std::multimap<Key, Value>::iterator TableIterator;
+ typedef typename std::list<TableIterator>::iterator ListIterator;
+
+ const size_t max_numer_of_items_;
+ std::multimap<Key, Value> table_;
+ std::list<TableIterator> insert_order_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicMaxSizedMap);
+};
+
+} // namespace net
+#endif // NET_QUIC_CONGESTION_CONTROL_QUIC_MAX_SIZED_MAP_H_
diff --git a/net/quic/congestion_control/quic_max_sized_map_test.cc b/net/quic/congestion_control/quic_max_sized_map_test.cc
new file mode 100644
index 0000000..89c05cc
--- /dev/null
+++ b/net/quic/congestion_control/quic_max_sized_map_test.cc
@@ -0,0 +1,66 @@
+// 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 "base/logging.h"
+#include "net/quic/congestion_control/quic_max_sized_map.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class QuicMaxSizedMapTest : public ::testing::Test {
+};
+
+TEST_F(QuicMaxSizedMapTest, Basic) {
+ QuicMaxSizedMap<int, int> test_map(100);
+ EXPECT_EQ(100u, test_map.MaxSize());
+ EXPECT_EQ(0u, test_map.Size());
+ test_map.Insert(1, 2);
+ test_map.Insert(1, 3);
+ EXPECT_EQ(100u, test_map.MaxSize());
+ EXPECT_EQ(2u, test_map.Size());
+ test_map.RemoveAll();
+ EXPECT_EQ(100u, test_map.MaxSize());
+ EXPECT_EQ(0u, test_map.Size());
+}
+
+TEST_F(QuicMaxSizedMapTest, Find) {
+ QuicMaxSizedMap<int, int> test_map(100);
+ test_map.Insert(1, 2);
+ test_map.Insert(1, 3);
+ test_map.Insert(2, 4);
+ test_map.Insert(3, 5);
+ QuicMaxSizedMap<int, int>::ConstIterator it = test_map.Find(2);
+ EXPECT_TRUE(it != test_map.End());
+ EXPECT_EQ(4, it->second);
+ it = test_map.Find(1);
+ EXPECT_TRUE(it != test_map.End());
+ EXPECT_EQ(2, it->second);
+ ++it;
+ EXPECT_TRUE(it != test_map.End());
+ EXPECT_EQ(3, it->second);
+}
+
+TEST_F(QuicMaxSizedMapTest, Sort) {
+ QuicMaxSizedMap<int, int> test_map(100);
+ test_map.Insert(9, 9);
+ test_map.Insert(8, 8);
+ test_map.Insert(7, 7);
+ test_map.Insert(6, 6);
+ test_map.Insert(2, 2);
+ test_map.Insert(4, 4);
+ test_map.Insert(5, 5);
+ test_map.Insert(3, 3);
+ test_map.Insert(0, 0);
+ test_map.Insert(1, 1);
+ QuicMaxSizedMap<int, int>::ConstIterator it = test_map.Begin();
+ for (int i = 0; i < 10; ++i, ++it) {
+ EXPECT_TRUE(it != test_map.End());
+ EXPECT_EQ(i, it->first);
+ EXPECT_EQ(i, it->second);
+ }
+}
+
+} // namespace test
+} // namespace net