Add QUIC congestion control classes. (Just a basic fixed rate sender.)
Review URL: https://chromiumcodereview.appspot.com/11236079
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@164486 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/net/net.gyp b/net/net.gyp
index f8ef0ea..b6697b7 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -633,6 +633,18 @@
'proxy/sync_host_resolver.h',
'proxy/sync_host_resolver_bridge.cc',
'proxy/sync_host_resolver_bridge.h',
+ 'quic/congestion_control/fix_rate_receiver.cc',
+ 'quic/congestion_control/fix_rate_receiver.h',
+ 'quic/congestion_control/fix_rate_sender.cc',
+ 'quic/congestion_control/fix_rate_sender.h',
+ 'quic/congestion_control/quic_receipt_metrics_collector.cc',
+ 'quic/congestion_control/quic_receipt_metrics_collector.h',
+ 'quic/congestion_control/quic_send_scheduler.cc',
+ 'quic/congestion_control/quic_send_scheduler.h',
+ 'quic/congestion_control/receive_algorithm_interface.cc',
+ 'quic/congestion_control/receive_algorithm_interface.h',
+ 'quic/congestion_control/send_algorithm_interface.cc',
+ 'quic/congestion_control/send_algorithm_interface.h',
'quic/crypto/crypto_framer.cc',
'quic/crypto/crypto_framer.h',
'quic/crypto/crypto_protocol.cc',
@@ -643,6 +655,8 @@
'quic/crypto/quic_decrypter.cc',
'quic/crypto/quic_encrypter.h',
'quic/crypto/quic_encrypter.cc',
+ 'quic/quic_clock.cc',
+ 'quic/quic_clock.h',
'quic/quic_data_reader.cc',
'quic/quic_data_reader.h',
'quic/quic_data_writer.cc',
@@ -1381,9 +1395,14 @@
'proxy/proxy_server_unittest.cc',
'proxy/proxy_service_unittest.cc',
'proxy/sync_host_resolver_bridge_unittest.cc',
+ 'quic/congestion_control/fix_rate_test.cc',
+ 'quic/congestion_control/quic_receipt_metrics_collector_test.cc',
+ 'quic/congestion_control/quic_send_scheduler_test.cc',
'quic/crypto/crypto_framer_test.cc',
'quic/crypto/null_decrypter_test.cc',
'quic/crypto/null_encrypter_test.cc',
+ 'quic/test_tools/mock_clock.cc',
+ 'quic/test_tools/mock_clock.h',
'quic/test_tools/quic_test_utils.cc',
'quic/test_tools/quic_test_utils.h',
'quic/quic_fec_group_test.cc',
diff --git a/net/quic/congestion_control/fix_rate_receiver.cc b/net/quic/congestion_control/fix_rate_receiver.cc
new file mode 100644
index 0000000..5adfc6ce
--- /dev/null
+++ b/net/quic/congestion_control/fix_rate_receiver.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/fix_rate_receiver.h"
+
+#include "base/basictypes.h"
+#include "net/quic/congestion_control/receive_algorithm_interface.h"
+
+namespace {
+ static const int kInitialBitrate = 100000; // In bytes per second.
+}
+
+namespace net {
+
+FixRateReceiver::FixRateReceiver()
+ : bitrate_in_bytes_per_second_(kInitialBitrate) {
+}
+
+bool FixRateReceiver::GenerateCongestionInfo(CongestionInfo* congestion_info) {
+ congestion_info->type = kFixRate;
+ congestion_info->fix_rate.bitrate_in_bytes_per_second =
+ bitrate_in_bytes_per_second_;
+ return true;
+}
+
+void FixRateReceiver::RecordIncomingPacket(
+ size_t bytes,
+ QuicPacketSequenceNumber sequence_number,
+ uint64 timestamp_us,
+ bool recovered) {
+ // Nothing to do for this simple implementation.
+}
+
+void FixRateReceiver::SetBitrate(int bytes_per_second) {
+ bitrate_in_bytes_per_second_ = bytes_per_second;
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/fix_rate_receiver.h b/net/quic/congestion_control/fix_rate_receiver.h
new file mode 100644
index 0000000..f212217
--- /dev/null
+++ b/net/quic/congestion_control/fix_rate_receiver.h
@@ -0,0 +1,38 @@
+// 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.
+//
+// Fix rate receive side congestion control, used for initial testing.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_FIX_RATE_RECEIVER_H_
+#define NET_QUIC_CONGESTION_CONTROL_FIX_RATE_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"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE FixRateReceiver : public ReceiveAlgorithmInterface {
+ public:
+ FixRateReceiver();
+
+ // Implements ReceiveAlgorithmInterface.
+ virtual bool GenerateCongestionInfo(CongestionInfo* congestion_info) OVERRIDE;
+
+ // Implements ReceiveAlgorithmInterface.
+ virtual void RecordIncomingPacket(size_t bytes,
+ QuicPacketSequenceNumber sequence_number,
+ uint64 timestamp_us,
+ bool recovered) OVERRIDE;
+
+ void SetBitrate(int bytes_per_second); // Used for testing only.
+
+ private:
+ int bitrate_in_bytes_per_second_;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_FIX_RATE_RECEIVER_H_
diff --git a/net/quic/congestion_control/fix_rate_sender.cc b/net/quic/congestion_control/fix_rate_sender.cc
new file mode 100644
index 0000000..e9af848
--- /dev/null
+++ b/net/quic/congestion_control/fix_rate_sender.cc
@@ -0,0 +1,88 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/fix_rate_sender.h"
+
+#include <math.h>
+
+#include "base/logging.h"
+#include "net/quic/quic_protocol.h"
+
+namespace {
+ static const int kInitialBitrate = 100000; // In bytes per second.
+ static const int kNoBytesSent = 0;
+}
+
+namespace net {
+
+FixRateSender::FixRateSender(QuicClock* clock)
+ : bitrate_in_bytes_per_second_(kInitialBitrate),
+ clock_(clock),
+ time_last_sent_us_(0),
+ bytes_last_sent_(kNoBytesSent),
+ bytes_in_flight_(0) {
+ DLOG(INFO) << "FixRateSender";
+}
+
+void FixRateSender::OnIncomingCongestionInfo(
+ const CongestionInfo& congestion_info) {
+ DCHECK(congestion_info.type == kFixRate) <<
+ "Invalid incoming CongestionFeedbackType:" << congestion_info.type;
+ if (congestion_info.type == kFixRate) {
+ bitrate_in_bytes_per_second_ =
+ congestion_info.fix_rate.bitrate_in_bytes_per_second;
+ }
+ // Silently ignore invalid messages in release mode.
+}
+
+void FixRateSender::OnIncomingAck(
+ QuicPacketSequenceNumber /*acked_sequence_number*/,
+ size_t bytes_acked, uint64 /*rtt_us*/) {
+ bytes_in_flight_ -= bytes_acked;
+}
+
+void FixRateSender::OnIncomingLoss(int /*number_of_lost_packets*/) {
+ // Ignore losses for fix rate sender.
+}
+
+void FixRateSender::SentPacket(QuicPacketSequenceNumber /*sequence_number*/,
+ size_t bytes,
+ bool retransmit) {
+ if (bytes > 0) {
+ time_last_sent_us_ = clock_->NowInUsec();
+ bytes_last_sent_ = bytes;
+ }
+ if (!retransmit) {
+ bytes_in_flight_ += bytes;
+ }
+}
+
+int FixRateSender::TimeUntilSend(bool /*retransmit*/) {
+ if (time_last_sent_us_ == 0 && bytes_last_sent_ == kNoBytesSent) {
+ // No sent packets.
+ return 0; // We can send now.
+ }
+ uint64 elapsed_time_us = clock_->NowInUsec() - time_last_sent_us_;
+ uint64 time_to_transmit_us = (bytes_last_sent_ * 1000000) /
+ bitrate_in_bytes_per_second_;
+ if (elapsed_time_us > time_to_transmit_us) {
+ return 0; // We can send now.
+ }
+ return time_to_transmit_us - elapsed_time_us;
+}
+
+size_t FixRateSender::AvailableCongestionWindow() {
+ if (TimeUntilSend(false) > 0) {
+ return 0;
+ }
+ // Note: Since this is for testing only we have the total window size equal to
+ // kMaxPacketSize.
+ return kMaxPacketSize - bytes_in_flight_;
+}
+
+int FixRateSender::BandwidthEstimate() {
+ return bitrate_in_bytes_per_second_;
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/fix_rate_sender.h b/net/quic/congestion_control/fix_rate_sender.h
new file mode 100644
index 0000000..c60a1943
--- /dev/null
+++ b/net/quic/congestion_control/fix_rate_sender.h
@@ -0,0 +1,46 @@
+// 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.
+//
+// Fix rate send side congestion control, used for testing.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_FIX_RATE_SENDER_H_
+#define NET_QUIC_CONGESTION_CONTROL_FIX_RATE_SENDER_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/congestion_control/send_algorithm_interface.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE FixRateSender : public SendAlgorithmInterface {
+ public:
+ explicit FixRateSender(QuicClock* clock);
+
+ // Start implementation of SendAlgorithmInterface.
+ virtual void OnIncomingCongestionInfo(
+ const CongestionInfo& congestion_info) OVERRIDE;
+ virtual void OnIncomingAck(QuicPacketSequenceNumber acked_sequence_number,
+ size_t acked_bytes,
+ uint64 rtt_us) OVERRIDE;
+ virtual void OnIncomingLoss(int number_of_lost_packets) OVERRIDE;
+ virtual void SentPacket(QuicPacketSequenceNumber equence_number,
+ size_t bytes, bool retransmit) OVERRIDE;
+ virtual int TimeUntilSend(bool retransmit) OVERRIDE;
+ virtual size_t AvailableCongestionWindow() OVERRIDE;
+ virtual int BandwidthEstimate() OVERRIDE;
+ // End implementation of SendAlgorithmInterface.
+
+ private:
+ uint32 bitrate_in_bytes_per_second_;
+ QuicClock* clock_;
+ uint64 time_last_sent_us_;
+ int bytes_last_sent_;
+ size_t bytes_in_flight_;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_FIX_RATE_SENDER_H_
diff --git a/net/quic/congestion_control/fix_rate_test.cc b/net/quic/congestion_control/fix_rate_test.cc
new file mode 100644
index 0000000..3372c022
--- /dev/null
+++ b/net/quic/congestion_control/fix_rate_test.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cmath>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/congestion_control/fix_rate_receiver.h"
+#include "net/quic/congestion_control/fix_rate_sender.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/quic_protocol.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+class FixRateTest : public ::testing::Test {
+ protected:
+ void SetUp() {
+ sender_.reset(new FixRateSender(&clock_));
+ receiver_.reset(new FixRateReceiver());
+ }
+ MockClock clock_;
+ scoped_ptr<FixRateSender> sender_;
+ scoped_ptr<FixRateReceiver> receiver_;
+};
+
+TEST_F(FixRateTest, ReceiverAPI) {
+ CongestionInfo info;
+ receiver_->SetBitrate(300000); // Bytes per second.
+ receiver_->RecordIncomingPacket(1, 1, 1, false);
+ ASSERT_TRUE(receiver_->GenerateCongestionInfo(&info));
+ EXPECT_EQ(kFixRate, info.type);
+ EXPECT_EQ(300000u, info.fix_rate.bitrate_in_bytes_per_second);
+}
+
+TEST_F(FixRateTest, SenderAPI) {
+ CongestionInfo info;
+ info.type = kFixRate;
+ info.fix_rate.bitrate_in_bytes_per_second = 300000;
+ sender_->OnIncomingCongestionInfo(info);
+ EXPECT_EQ(0, sender_->TimeUntilSend(false));
+ EXPECT_EQ(kMaxPacketSize, sender_->AvailableCongestionWindow());
+ sender_->SentPacket(1, 1200, false);
+ EXPECT_EQ(0u, sender_->AvailableCongestionWindow());
+ EXPECT_EQ(300000, sender_->BandwidthEstimate());
+ EXPECT_EQ(4000, sender_->TimeUntilSend(false));
+ clock_.AdvanceTime(0.002);
+ EXPECT_EQ(2000, sender_->TimeUntilSend(false));
+ clock_.AdvanceTime(0.002);
+ EXPECT_EQ(0, sender_->TimeUntilSend(false));
+}
+
+TEST_F(FixRateTest, Pacing) {
+ const int packet_size = 1200;
+ const int rtt_us = 30000;
+ CongestionInfo info;
+ receiver_->SetBitrate(240000); // Bytes per second.
+ ASSERT_TRUE(receiver_->GenerateCongestionInfo(&info));
+ sender_->OnIncomingCongestionInfo(info);
+ double acc_advance_time = 0.0;
+ for (int i = 0; i < 100; ++i) {
+ EXPECT_EQ(0, sender_->TimeUntilSend(false));
+ EXPECT_EQ(kMaxPacketSize, sender_->AvailableCongestionWindow());
+ sender_->SentPacket(i, packet_size, false);
+ double advance_time = sender_->TimeUntilSend(false) / 1000000.0;
+ clock_.AdvanceTime(advance_time);
+ sender_->OnIncomingAck(i, packet_size, rtt_us);
+ acc_advance_time += advance_time;
+ }
+ EXPECT_EQ(500, floor((acc_advance_time * 1000) + 0.5));
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/quic_receipt_metrics_collector.cc b/net/quic/congestion_control/quic_receipt_metrics_collector.cc
new file mode 100644
index 0000000..db98e4d
--- /dev/null
+++ b/net/quic/congestion_control/quic_receipt_metrics_collector.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/quic_receipt_metrics_collector.h"
+
+#include "net/quic/congestion_control/receive_algorithm_interface.h"
+
+namespace net {
+
+QuicReceiptMetricsCollector::QuicReceiptMetricsCollector(
+ QuicClock* clock,
+ CongestionFeedbackType type)
+ : receive_algorithm_(ReceiveAlgorithmInterface::Create(clock, type)) {
+}
+
+QuicReceiptMetricsCollector::~QuicReceiptMetricsCollector() {
+}
+
+bool QuicReceiptMetricsCollector::GenerateCongestionInfo(
+ CongestionInfo* info) {
+ return receive_algorithm_->GenerateCongestionInfo(info);
+}
+
+void QuicReceiptMetricsCollector::RecordIncomingPacket(
+ size_t bytes,
+ QuicPacketSequenceNumber sequence_number,
+ uint64 timestamp_us,
+ bool revived) {
+ receive_algorithm_->RecordIncomingPacket(bytes, sequence_number, timestamp_us,
+ revived);
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/quic_receipt_metrics_collector.h b/net/quic/congestion_control/quic_receipt_metrics_collector.h
new file mode 100644
index 0000000..7ae77d2
--- /dev/null
+++ b/net/quic/congestion_control/quic_receipt_metrics_collector.h
@@ -0,0 +1,57 @@
+// 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.
+//
+// This is the base class for QUIC receive side congestion control.
+// This class will provide the CongestionInfo objects to outgoing ACKs if
+// needed.
+// The acctual receive side algorithm is implemented via the
+// ReceiveAlgorithmInterface.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_QUIC_RECEIPT_METRICS_COLLECTOR_H_
+#define NET_QUIC_CONGESTION_CONTROL_QUIC_RECEIPT_METRICS_COLLECTOR_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.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 QuicReceiptMetricsCollector {
+ public:
+ QuicReceiptMetricsCollector(QuicClock* clock,
+ CongestionFeedbackType congestion_type);
+
+ virtual ~QuicReceiptMetricsCollector();
+
+ // Should be called for each ACK packet to decide if we need to attach a
+ // CongestionInfo block.
+ // Returns false if no CongestionInfo block is needed otherwise fills in
+ // congestion_info and return true.
+ virtual bool GenerateCongestionInfo(CongestionInfo* congestion_info);
+
+ // Should be called for each incoming packet.
+ // bytes: is the packet size in bytes including IP headers.
+ // sequence_number: is the unique sequence number from the QUIC packet header.
+ // timestamp: is the sent timestamp from the QUIC packet header.
+ // TODO(pwestin) which type should we use for timestamp?
+ // revived: is set if the packet is lost and then recovered with help of a
+ // FEC packet.
+ virtual void RecordIncomingPacket(size_t bytes,
+ QuicPacketSequenceNumber sequence_number,
+ uint64 timestamp_us,
+ bool revived);
+
+ // TODO(pwestin) Keep track of the number of FEC recovered packets.
+ // Needed by all congestion control algorithms.
+
+ private:
+ scoped_ptr<ReceiveAlgorithmInterface> receive_algorithm_;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_QUIC_RECEIPT_METRICS_COLLECTOR_H_
diff --git a/net/quic/congestion_control/quic_receipt_metrics_collector_test.cc b/net/quic/congestion_control/quic_receipt_metrics_collector_test.cc
new file mode 100644
index 0000000..1388130
--- /dev/null
+++ b/net/quic/congestion_control/quic_receipt_metrics_collector_test.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/congestion_control/quic_receipt_metrics_collector.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+class QuicReceiptMetricsCollectorTest : public ::testing::Test {
+ protected:
+ void SetUpCongestionType(CongestionFeedbackType congestion_type) {
+ receiver_.reset(new QuicReceiptMetricsCollector(&clock_, congestion_type));
+ }
+
+ MockClock clock_;
+ scoped_ptr<QuicReceiptMetricsCollector> receiver_;
+};
+
+TEST_F(QuicReceiptMetricsCollectorTest, FixedRateReceiverAPI) {
+ SetUpCongestionType(kFixRate);
+ CongestionInfo info;
+ receiver_->RecordIncomingPacket(1, 1, 1, false);
+ ASSERT_TRUE(receiver_->GenerateCongestionInfo(&info));
+ EXPECT_EQ(kFixRate, info.type);
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/quic_send_scheduler.cc b/net/quic/congestion_control/quic_send_scheduler.cc
new file mode 100644
index 0000000..9df5db7
--- /dev/null
+++ b/net/quic/congestion_control/quic_send_scheduler.cc
@@ -0,0 +1,193 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "net/quic/congestion_control/quic_send_scheduler.h"
+
+#include <cmath>
+
+#include "base/stl_util.h"
+#include "base/time.h"
+#include "net/quic/congestion_control/send_algorithm_interface.h"
+//#include "util/gtl/map-util.h"
+
+namespace net {
+
+// To prevent too aggressive pacing we allow the following packet burst size.
+const int kMinPacketBurstSize = 2;
+const int64 kNumMicrosPerSecond = base::Time::kMicrosecondsPerSecond;
+
+QuicSendScheduler::QuicSendScheduler(
+ QuicClock* clock,
+ CongestionFeedbackType type)
+ : clock_(clock),
+ current_estimated_bandwidth_(-1),
+ max_estimated_bandwidth_(-1),
+ last_sent_packet_us_(0),
+ current_packet_bucket_(-1),
+ first_packet_bucket_(-1),
+ send_algorithm_(SendAlgorithmInterface::Create(clock, type)) {
+ memset(packet_history_, 0, sizeof(packet_history_));
+}
+
+QuicSendScheduler::~QuicSendScheduler() {
+ STLDeleteContainerPairSecondPointers(pending_packets_.begin(),
+ pending_packets_.end());
+}
+
+int QuicSendScheduler::UpdatePacketHistory() {
+ uint64 now_us = clock_->NowInUsec();
+ int timestamp_scaled = now_us / kBitrateSmoothingPeriod;
+
+ int bucket = timestamp_scaled % kBitrateSmoothingBuckets;
+ if (!HasSentPacket()) {
+ // First packet.
+ current_packet_bucket_ = bucket;
+ first_packet_bucket_ = bucket;
+ }
+ if (current_packet_bucket_ != bucket) {
+ // We need to make sure to zero out any skipped buckets.
+ // Max loop count is kBitrateSmoothingBuckets.
+ do {
+ current_packet_bucket_ =
+ (1 + current_packet_bucket_) % kBitrateSmoothingBuckets;
+ packet_history_[current_packet_bucket_] = 0;
+ if (first_packet_bucket_ == current_packet_bucket_) {
+ // We have filled the whole window, no need to keep track of first
+ // bucket.
+ first_packet_bucket_ = -1;
+ }
+ } while (current_packet_bucket_ != bucket);
+ }
+ return bucket;
+}
+
+void QuicSendScheduler::SentPacket(QuicPacketSequenceNumber sequence_number,
+ size_t bytes,
+ bool retransmit) {
+ int bucket = UpdatePacketHistory();
+ packet_history_[bucket] += bytes;
+ last_sent_packet_us_ = clock_->NowInUsec();
+ send_algorithm_->SentPacket(sequence_number, bytes, retransmit);
+ if (!retransmit) {
+ pending_packets_[sequence_number] = new PendingPacket(bytes,
+ last_sent_packet_us_);
+ }
+ DLOG(INFO) << "Sent sequence number:" << sequence_number;
+}
+
+void QuicSendScheduler::OnIncomingAckFrame(const QuicAckFrame& ack_frame) {
+ if (ack_frame.congestion_info.type != kNone) {
+ send_algorithm_->OnIncomingCongestionInfo(ack_frame.congestion_info);
+ }
+ // We want to.
+ // * Get all packets lower(including) than largest_received
+ // from pending_packets_.
+ // * Remove all missing packets.
+ // * Send each ACK in the list to send_algorithm_.
+ uint64 last_timestamp_us = 0;
+ std::map<QuicPacketSequenceNumber, size_t> acked_packets;
+
+ PendingPacketsMap::iterator it, it_upper;
+ it = pending_packets_.begin();
+ it_upper = pending_packets_.upper_bound(
+ ack_frame.received_info.largest_received);
+
+ while (it != it_upper) {
+ QuicPacketSequenceNumber sequence_number = it->first;
+ if (ack_frame.received_info.missing_packets.find(sequence_number) ==
+ ack_frame.received_info.missing_packets.end()) {
+ // Not missing, hence implicitly acked.
+ scoped_ptr<PendingPacket> pending_packet_cleaner(it->second);
+ acked_packets[sequence_number] = pending_packet_cleaner->BytesSent();
+ last_timestamp_us = pending_packet_cleaner->SendTimestamp();
+ pending_packets_.erase(it++); // Must be incremented post to work.
+ } else {
+ ++it;
+ }
+ }
+ // We calculate the RTT based on the highest ACKed sequence number, the lower
+ // sequence numbers will include the ACK aggregation delay.
+ uint64 rtt_us = clock_->NowInUsec() - last_timestamp_us;
+
+ std::map<QuicPacketSequenceNumber, size_t>::iterator it_acked_packets;
+ for (it_acked_packets = acked_packets.begin();
+ it_acked_packets != acked_packets.end();
+ ++it_acked_packets) {
+ send_algorithm_->OnIncomingAck(it_acked_packets->first,
+ it_acked_packets->second,
+ rtt_us);
+ DLOG(INFO) << "ACKed sequence number:" << it_acked_packets->first;
+ }
+}
+
+int QuicSendScheduler::TimeUntilSend(bool retransmit) {
+ return send_algorithm_->TimeUntilSend(retransmit);
+}
+
+size_t QuicSendScheduler::AvailableCongestionWindow() {
+ size_t available_congestion_window =
+ send_algorithm_->AvailableCongestionWindow();
+ DLOG(INFO) << "Available congestion window:" << available_congestion_window;
+
+ // Should we limit the window to pace the data?
+ if (available_congestion_window > kMinPacketBurstSize * kMaxPacketSize) {
+ // TODO(pwestin): implement pacing.
+ // will depend on estimated bandwidth; higher bandwidth => larger burst
+ // we need to consider our timing accuracy here too.
+ // an accuracy of 1ms will allow us to send up to 19.2Mbit/s with 2 packets
+ // per burst.
+ }
+ return available_congestion_window;
+}
+
+int QuicSendScheduler::BandwidthEstimate() {
+ return send_algorithm_->BandwidthEstimate();
+}
+
+bool QuicSendScheduler::HasSentPacket() {
+ return (current_packet_bucket_ != -1);
+}
+
+// TODO(pwestin) add a timer to make this accurate even if not called.
+int QuicSendScheduler::SentBandwidth() {
+ UpdatePacketHistory();
+
+ if (first_packet_bucket_ != -1) {
+ // We don't have a full set of data.
+ int number_of_buckets = (current_packet_bucket_ - first_packet_bucket_) + 1;
+ if (number_of_buckets < 0) {
+ // We have a wrap in bucket index.
+ number_of_buckets = kBitrateSmoothingBuckets + number_of_buckets;
+ }
+ int64 sum = 0;
+ int bucket = first_packet_bucket_;
+ for (int n = 0; n < number_of_buckets; bucket++, n++) {
+ bucket = bucket % kBitrateSmoothingBuckets;
+ sum += packet_history_[bucket];
+ }
+ current_estimated_bandwidth_ = (sum *
+ (kNumMicrosPerSecond / kBitrateSmoothingPeriod)) / number_of_buckets;
+ } else {
+ int64 sum = 0;
+ for (uint32 bucket = 0; bucket < kBitrateSmoothingBuckets; ++bucket) {
+ sum += packet_history_[bucket];
+ }
+ current_estimated_bandwidth_ = (sum * (kNumMicrosPerSecond /
+ kBitrateSmoothingPeriod)) / kBitrateSmoothingBuckets;
+ }
+ max_estimated_bandwidth_ = std::max(max_estimated_bandwidth_,
+ current_estimated_bandwidth_);
+ return current_estimated_bandwidth_;
+}
+
+int QuicSendScheduler::PeakSustainedBandwidth() {
+ // To make sure that we get the latest estimate we call SentBandwidth.
+ if (HasSentPacket()) {
+ SentBandwidth();
+ }
+ return max_estimated_bandwidth_;
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/quic_send_scheduler.h b/net/quic/congestion_control/quic_send_scheduler.h
new file mode 100644
index 0000000..2d1379ac
--- /dev/null
+++ b/net/quic/congestion_control/quic_send_scheduler.h
@@ -0,0 +1,108 @@
+// 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.
+//
+// The is the base class for QUIC send side congestion control.
+// It decides when we can send a QUIC packet to the wire.
+// This class handles the basic bookkeeping of sent bitrate and packet loss.
+// The acctual send side algorithm is implemented via the
+// SendAlgorithmInterface.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_QUIC_SEND_SCHEDULER_H_
+#define NET_QUIC_CONGESTION_CONTROL_QUIC_SEND_SCHEDULER_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/net_export.h"
+#include "net/quic/congestion_control/quic_receipt_metrics_collector.h"
+#include "net/quic/congestion_control/send_algorithm_interface.h"
+#include "net/quic/quic_clock.h"
+
+namespace net {
+
+const uint32 kBitrateSmoothingBuckets = 300;
+
+// 10 ms in micro seconds, must be less than 1 second for current
+// implementation due to overflow resulting in a potential divide by zero.
+const uint32 kBitrateSmoothingPeriod = 10000;
+
+// When kUnknownWaitTime is returned, there is no need to poll the function
+// again until we receive a new event.
+const int kUnknownWaitTime = -1;
+
+class NET_EXPORT_PRIVATE QuicSendScheduler {
+ public:
+ class PendingPacket {
+ public:
+ PendingPacket(size_t bytes, uint64 timestamp_us)
+ : bytes_sent_(bytes),
+ send_timestamp_us_(timestamp_us) {
+ }
+ size_t BytesSent() { return bytes_sent_; }
+ uint64 SendTimestamp() { return send_timestamp_us_; }
+
+ private:
+ size_t bytes_sent_;
+ uint64 send_timestamp_us_;
+ };
+ typedef std::map<QuicPacketSequenceNumber, PendingPacket*> PendingPacketsMap;
+
+ QuicSendScheduler(QuicClock* clock, CongestionFeedbackType congestion_type);
+ virtual ~QuicSendScheduler();
+
+ // Called when we have received an ack frame from remote peer.
+ virtual void OnIncomingAckFrame(const QuicAckFrame& ack_frame);
+
+ // Inform that we sent x bytest to the wire, and if that was a retransmission.
+ // Note: this function must be called for every packet sent to the wire.
+ virtual void SentPacket(QuicPacketSequenceNumber sequence_number,
+ size_t bytes,
+ bool retransmit);
+
+ // Calculate the time until we can send the next packet to the wire.
+ // Note 1: When kUnknownWaitTime is returned, there is no need to poll
+ // TimeUntilSend again until we receive an OnIncomingAckFrame event.
+ // Note 2: Send algorithms may or may not use |retransmit| in their
+ // calculations.
+ virtual int TimeUntilSend(bool retransmit);
+
+ // Returns the current available congestion window in bytes, the number of
+ // bytes that can be sent now.
+ // Note: due to pacing this function might return a smaller value than the
+ // real available congestion window. This way we hold off the sender to avoid
+ // queuing in the lower layers in the stack.
+ size_t AvailableCongestionWindow();
+
+ int BandwidthEstimate(); // Current estimate, in bytes per second.
+
+ int SentBandwidth(); // Current smooth acctually sent, in bytes per second.
+
+ int PeakSustainedBandwidth(); // In bytes per second.
+
+ private:
+ // Have we sent any packets during this session?
+ bool HasSentPacket();
+ int UpdatePacketHistory();
+
+ QuicClock* clock_;
+ int current_estimated_bandwidth_;
+ int max_estimated_bandwidth_;
+ uint64 last_sent_packet_us_;
+ // To keep track of the real sent bitrate we keep track of the last sent bytes
+ // by keeping an array containing the number of bytes sent in a short timespan
+ // kBitrateSmoothingPeriod; multiple of these buckets kBitrateSmoothingBuckets
+ // create a time window in which we calculate the average bitrate.
+ int current_packet_bucket_; // Last active bucket in window.
+ int first_packet_bucket_; // First active bucket in window.
+ uint32 packet_history_[kBitrateSmoothingBuckets]; // The window.
+ scoped_ptr<SendAlgorithmInterface> send_algorithm_;
+ // TODO(pwestin): should we combine the packet_history_ bucket with this map?
+ // For now I keep it separate for easy implementation.
+ PendingPacketsMap pending_packets_;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_QUIC_SEND_SCHEDULER_H_
diff --git a/net/quic/congestion_control/quic_send_scheduler_test.cc b/net/quic/congestion_control/quic_send_scheduler_test.cc
new file mode 100644
index 0000000..f25adda
--- /dev/null
+++ b/net/quic/congestion_control/quic_send_scheduler_test.cc
@@ -0,0 +1,149 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/quic_send_scheduler.h"
+
+#include <cmath>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/congestion_control/quic_receipt_metrics_collector.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/quic_protocol.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+class QuicSendSchedulerTest : public ::testing::Test {
+ protected:
+ void SetUpCongestionType(CongestionFeedbackType congestion_type) {
+ sender_.reset(new QuicSendScheduler(&clock_, congestion_type));
+ }
+
+ MockClock clock_;
+ scoped_ptr<QuicSendScheduler> sender_;
+};
+
+TEST_F(QuicSendSchedulerTest, FixedRateSenderAPI) {
+ SetUpCongestionType(kFixRate);
+ QuicAckFrame ack;
+ ack.congestion_info.type = kFixRate;
+ ack.congestion_info.fix_rate.bitrate_in_bytes_per_second = 300000;
+ sender_->OnIncomingAckFrame(ack);
+ EXPECT_EQ(-1, sender_->PeakSustainedBandwidth());
+ EXPECT_EQ(0, sender_->TimeUntilSend(false));
+ EXPECT_EQ(kMaxPacketSize, sender_->AvailableCongestionWindow());
+ sender_->SentPacket(1, 1200, false);
+ EXPECT_EQ(0u, sender_->AvailableCongestionWindow());
+ EXPECT_EQ(4000, sender_->TimeUntilSend(false));
+ clock_.AdvanceTime(0.002);
+ EXPECT_EQ(2000, sender_->TimeUntilSend(false));
+ clock_.AdvanceTime(0.002);
+ EXPECT_EQ(0, sender_->TimeUntilSend(false));
+}
+
+TEST_F(QuicSendSchedulerTest, FixedRatePacing) {
+ SetUpCongestionType(kFixRate);
+ QuicAckFrame ack;
+ ack.congestion_info.type = kFixRate;
+ ack.congestion_info.fix_rate.bitrate_in_bytes_per_second = 100000;
+ ack.received_info.largest_received = 0;
+ sender_->OnIncomingAckFrame(ack);
+ double acc_advance_time = 0.0;
+ for (int i = 0; i < 100; ++i) {
+ EXPECT_EQ(0, sender_->TimeUntilSend(false));
+ EXPECT_EQ(kMaxPacketSize, sender_->AvailableCongestionWindow());
+ sender_->SentPacket(i, 1200, false);
+ double advance_time = sender_->TimeUntilSend(false) / 1000000.0;
+ clock_.AdvanceTime(advance_time);
+ acc_advance_time += advance_time;
+ // Ack the packet we sent.
+ ack.received_info.largest_received = i;
+ sender_->OnIncomingAckFrame(ack);
+ }
+ EXPECT_EQ(1200, floor((acc_advance_time * 1000) + 0.5));
+}
+
+TEST_F(QuicSendSchedulerTest, AvailableCongestionWindow) {
+ SetUpCongestionType(kFixRate);
+ QuicAckFrame ack;
+ ack.congestion_info.type = kFixRate;
+ ack.congestion_info.fix_rate.bitrate_in_bytes_per_second = 100000;
+ sender_->OnIncomingAckFrame(ack);
+ EXPECT_EQ(0, sender_->TimeUntilSend(false));
+ EXPECT_EQ(kMaxPacketSize, sender_->AvailableCongestionWindow());
+ for (int i = 1; i <= 12; i++) {
+ sender_->SentPacket(i, 100, false);
+ double advance_time = sender_->TimeUntilSend(false) / 1000000.0;
+ clock_.AdvanceTime(advance_time);
+ EXPECT_EQ(kMaxPacketSize - (i * 100), sender_->AvailableCongestionWindow());
+ }
+ // Ack the packet we sent.
+ ack.received_info.largest_received = 12;
+ sender_->OnIncomingAckFrame(ack);
+ EXPECT_EQ(kMaxPacketSize, sender_->AvailableCongestionWindow());
+}
+
+TEST_F(QuicSendSchedulerTest, FixedRateBandwidth) {
+ SetUpCongestionType(kFixRate);
+ QuicAckFrame ack;
+ ack.congestion_info.type = kFixRate;
+ ack.congestion_info.fix_rate.bitrate_in_bytes_per_second = 100000;
+ sender_->OnIncomingAckFrame(ack);
+ for (int i = 0; i < 100; ++i) {
+ double advance_time = sender_->TimeUntilSend(false) / 1000000.0;
+ clock_.AdvanceTime(advance_time);
+ EXPECT_EQ(0, sender_->TimeUntilSend(false));
+ EXPECT_EQ(kMaxPacketSize, sender_->AvailableCongestionWindow());
+ sender_->SentPacket(i, 1000, false);
+ // Ack the packet we sent.
+ ack.received_info.largest_received = i;
+ sender_->OnIncomingAckFrame(ack);
+ }
+ EXPECT_EQ(100000, sender_->BandwidthEstimate());
+ EXPECT_EQ(100000, sender_->PeakSustainedBandwidth());
+ EXPECT_EQ(100000, sender_->SentBandwidth());
+}
+
+TEST_F(QuicSendSchedulerTest, BandwidthWith3SecondGap) {
+ SetUpCongestionType(kFixRate);
+ QuicAckFrame ack;
+ ack.congestion_info.type = kFixRate;
+ ack.congestion_info.fix_rate.bitrate_in_bytes_per_second = 100000;
+ sender_->OnIncomingAckFrame(ack);
+ for (int i = 0; i < 100; ++i) {
+ double advance_time = sender_->TimeUntilSend(false) / 1000000.0;
+ clock_.AdvanceTime(advance_time);
+ EXPECT_EQ(0, sender_->TimeUntilSend(false));
+ EXPECT_EQ(kMaxPacketSize, sender_->AvailableCongestionWindow());
+ sender_->SentPacket(i, 1000, false);
+ // Ack the packet we sent.
+ ack.received_info.largest_received = i;
+ sender_->OnIncomingAckFrame(ack);
+ }
+ EXPECT_EQ(100000, sender_->BandwidthEstimate());
+ EXPECT_EQ(100000, sender_->PeakSustainedBandwidth());
+ EXPECT_EQ(100000, sender_->SentBandwidth());
+ clock_.AdvanceTime(1.0);
+ EXPECT_EQ(50000, sender_->SentBandwidth());
+ clock_.AdvanceTime(2.1);
+ EXPECT_EQ(100000, sender_->BandwidthEstimate());
+ EXPECT_EQ(100000, sender_->PeakSustainedBandwidth());
+ EXPECT_EQ(0, sender_->SentBandwidth());
+ for (int i = 0; i < 150; ++i) {
+ EXPECT_EQ(0, sender_->TimeUntilSend(false));
+ EXPECT_EQ(kMaxPacketSize, sender_->AvailableCongestionWindow());
+ sender_->SentPacket(i + 100, 1000, false);
+ double advance_time = sender_->TimeUntilSend(false) / 1000000.0;
+ clock_.AdvanceTime(advance_time);
+ // Ack the packet we sent.
+ ack.received_info.largest_received = i + 100;
+ sender_->OnIncomingAckFrame(ack);
+ }
+ EXPECT_EQ(100000, sender_->BandwidthEstimate());
+ EXPECT_EQ(100000, sender_->PeakSustainedBandwidth());
+ EXPECT_EQ(50000, sender_->SentBandwidth());
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/receive_algorithm_interface.cc b/net/quic/congestion_control/receive_algorithm_interface.cc
new file mode 100644
index 0000000..3d6819b
--- /dev/null
+++ b/net/quic/congestion_control/receive_algorithm_interface.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/receive_algorithm_interface.h"
+
+#include "net/quic/congestion_control/fix_rate_receiver.h"
+
+namespace net {
+
+// Factory for receive side congestion control algorithm.
+ReceiveAlgorithmInterface* ReceiveAlgorithmInterface::Create(
+ QuicClock* clock,
+ CongestionFeedbackType type) {
+ switch (type) {
+ case kNone:
+ LOG(DFATAL) << "Attempted to create a ReceiveAlgorithm with kNone.";
+ break;
+ case kTCP:
+ //return new TcpReceiver(clock);
+ case kInterArrival:
+ break; // TODO(pwestin) Implement.
+ case kFixRate:
+ return new FixRateReceiver();
+ }
+ return NULL;
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/receive_algorithm_interface.h b/net/quic/congestion_control/receive_algorithm_interface.h
new file mode 100644
index 0000000..1acdb9f
--- /dev/null
+++ b/net/quic/congestion_control/receive_algorithm_interface.h
@@ -0,0 +1,42 @@
+// 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.
+//
+// The pure virtual class for receive side congestion algorithm.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_RECEIVE_ALGORITHM_INTERFACE_H_
+#define NET_QUIC_CONGESTION_CONTROL_RECEIVE_ALGORITHM_INTERFACE_H_
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE ReceiveAlgorithmInterface {
+ public:
+ static ReceiveAlgorithmInterface* Create(QuicClock* clock,
+ CongestionFeedbackType type);
+
+ virtual ~ReceiveAlgorithmInterface() {}
+
+ // Returns false if no CongestionInfo block is needed otherwise fills in
+ // congestion_info and return true.
+ virtual bool GenerateCongestionInfo(CongestionInfo* congestion_info) = 0;
+
+ // Should be called for each incoming packet.
+ // bytes: is the packet size in bytes including IP headers.
+ // sequence_number: is the unique sequence number from the QUIC packet header.
+ // timestamp_us: is the sent timestamp from the QUIC packet header.
+ // revived: is set if the packet is lost and then recovered with help of FEC
+ // (Forward Error Correction) packet(s).
+ virtual void RecordIncomingPacket(size_t bytes,
+ QuicPacketSequenceNumber sequence_number,
+ uint64 timestamp_us,
+ bool revived) = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_RECEIVE_ALGORITHM_INTERFACE_H_
diff --git a/net/quic/congestion_control/send_algorithm_interface.cc b/net/quic/congestion_control/send_algorithm_interface.cc
new file mode 100644
index 0000000..45e793e9
--- /dev/null
+++ b/net/quic/congestion_control/send_algorithm_interface.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/send_algorithm_interface.h"
+
+#include "net/quic/congestion_control/fix_rate_sender.h"
+
+namespace net {
+
+// TODO(pwestin): Change to cubic when implemented.
+const bool kUseReno = true;
+
+// Factory for send side congestion control algorithm.
+SendAlgorithmInterface* SendAlgorithmInterface::Create(
+ QuicClock* clock,
+ CongestionFeedbackType type) {
+ switch (type) {
+ case kNone:
+ LOG(DFATAL) << "Attempted to create a SendAlgorithm with kNone.";
+ break;
+ case kTCP:
+ //return new TcpCubicSender(clock, kUseReno);
+ case kInterArrival:
+ break; // TODO(pwestin) Implement.
+ case kFixRate:
+ return new FixRateSender(clock);
+ }
+ return NULL;
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/send_algorithm_interface.h b/net/quic/congestion_control/send_algorithm_interface.h
new file mode 100644
index 0000000..b2196616
--- /dev/null
+++ b/net/quic/congestion_control/send_algorithm_interface.h
@@ -0,0 +1,55 @@
+// 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.
+//
+// The pure virtual class for send side congestion control algorithm.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_INTERFACE_H_
+#define NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_INTERFACE_H_
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE SendAlgorithmInterface {
+ public:
+ static SendAlgorithmInterface* Create(QuicClock* clock,
+ CongestionFeedbackType type);
+
+ virtual ~SendAlgorithmInterface() {}
+
+ // Called when we receive congestion information from remote peer.
+ virtual void OnIncomingCongestionInfo(
+ const CongestionInfo& congestion_info) = 0;
+
+ // Called for each received ACK, with sequence number from remote peer.
+ virtual void OnIncomingAck(QuicPacketSequenceNumber acked_sequence_number,
+ size_t acked_bytes,
+ uint64 rtt_us) = 0;
+
+ virtual void OnIncomingLoss(int number_of_lost_packets) = 0;
+
+ // Inform that we sent x bytest to the wire, and if that was a retransmission.
+ // Note: this function must be called for every packet sent to the wire.
+ virtual void SentPacket(QuicPacketSequenceNumber sequence_number,
+ size_t bytes,
+ bool retransmit) = 0;
+
+ // Calculate the time until we can send the next packet.
+ // Usage: When this returns 0, CongestionWindow returns the number of bytes
+ // of the congestion window.
+ virtual int TimeUntilSend(bool retransmit) = 0;
+
+ // The current available congestion window in bytes.
+ virtual size_t AvailableCongestionWindow() = 0;
+
+ // What's the current estimated bandwidth in bytes per second.
+ virtual int BandwidthEstimate() = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_INTERFACE_H_
diff --git a/net/quic/quic_clock.cc b/net/quic/quic_clock.cc
new file mode 100644
index 0000000..0d7d54a3
--- /dev/null
+++ b/net/quic/quic_clock.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_clock.h"
+
+#include "base/time.h"
+
+namespace net {
+
+QuicClock::QuicClock() {
+}
+
+QuicClock::~QuicClock() {}
+
+uint64 QuicClock::NowInUsec() {
+ base::TimeDelta delta = base::Time::Now() - base::Time::UnixEpoch();
+ return delta.InMicroseconds();
+}
+
+} // namespace net
diff --git a/net/quic/quic_clock.h b/net/quic/quic_clock.h
new file mode 100644
index 0000000..df5f510
--- /dev/null
+++ b/net/quic/quic_clock.h
@@ -0,0 +1,29 @@
+// 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.
+
+#ifndef NET_QUIC_QUIC_CLOCK_H_
+#define NET_QUIC_QUIC_CLOCK_H_
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+typedef double WallTime;
+
+// Clock to efficiently retrieve an approximately accurate time from an
+// EpollServer.
+class NET_EXPORT_PRIVATE QuicClock {
+ public:
+ QuicClock();
+ virtual ~QuicClock();
+
+ // Returns the approximate current time as the number of microseconds
+ // since the Unix epoch.
+ virtual uint64 NowInUsec();
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_CLOCK_H_
diff --git a/net/quic/test_tools/mock_clock.cc b/net/quic/test_tools/mock_clock.cc
new file mode 100644
index 0000000..59067f4
--- /dev/null
+++ b/net/quic/test_tools/mock_clock.cc
@@ -0,0 +1,19 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/mock_clock.h"
+
+namespace net {
+
+MockClock::MockClock() : now_(0) {
+}
+
+MockClock::~MockClock() {
+}
+
+uint64 MockClock::NowInUsec() {
+ return now_;
+}
+
+} // namespace net
diff --git a/net/quic/test_tools/mock_clock.h b/net/quic/test_tools/mock_clock.h
new file mode 100644
index 0000000..3a69869
--- /dev/null
+++ b/net/quic/test_tools/mock_clock.h
@@ -0,0 +1,34 @@
+// 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.
+
+#ifndef NET_QUIC_TEST_TOOLS_MOCK_CLOCK_H_
+#define NET_QUIC_TEST_TOOLS_MOCK_CLOCK_H_
+
+#include "net/quic/quic_clock.h"
+
+#include "base/compiler_specific.h"
+#include "base/time.h"
+
+namespace net {
+
+class MockClock : public QuicClock {
+ public:
+ MockClock();
+
+ virtual ~MockClock();
+
+ virtual uint64 NowInUsec() OVERRIDE;
+
+ void AdvanceTime(WallTime delta) {
+ uint64 delta_us = delta * base::Time::kMicrosecondsPerSecond;
+ now_ += delta_us;
+ }
+
+ private:
+ uint64 now_;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_MOCK_CLOCK_H_