1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/quic/congestion_control/hybrid_slow_start.h"
6
7namespace net {
8
9// Note(pwestin): the magic clamping numbers come from the original code in
10// tcp_cubic.c.
11// Number of delay samples for detecting the increase of delay.
12const int kHybridStartMinSamples = 8;
13const int kHybridStartDelayFactorExp = 4;  // 2^4 = 16
14const int kHybridStartDelayMinThresholdUs = 2000;
15const int kHybridStartDelayMaxThresholdUs = 16000;
16
17HybridSlowStart::HybridSlowStart(const QuicClock* clock)
18    : clock_(clock),
19      started_(false),
20      found_ack_train_(false),
21      found_delay_(false),
22      round_start_(QuicTime::Zero()),
23      end_sequence_number_(0),
24      last_time_(QuicTime::Zero()),
25      sample_count_(0),
26      current_rtt_(QuicTime::Delta::Zero()) {
27}
28
29void HybridSlowStart::Restart() {
30  found_ack_train_ = false;
31  found_delay_  = false;
32}
33
34void HybridSlowStart::Reset(QuicPacketSequenceNumber end_sequence_number) {
35  DLOG(INFO) << "Reset hybrid slow start @" << end_sequence_number;
36  round_start_ = last_time_ = clock_->ApproximateNow();
37  end_sequence_number_ = end_sequence_number;
38  current_rtt_ = QuicTime::Delta::Zero();
39  sample_count_ = 0;
40  started_ = true;
41}
42
43bool HybridSlowStart::EndOfRound(QuicPacketSequenceNumber ack) {
44  return end_sequence_number_ <= ack;
45}
46
47void HybridSlowStart::Update(QuicTime::Delta rtt, QuicTime::Delta delay_min) {
48  // The original code doesn't invoke this until we hit 16 packet per burst.
49  // Since the code handles lower than 16 grecefully and I removed that
50  // limit.
51  if (found_ack_train_ || found_delay_) {
52    return;
53  }
54  QuicTime current_time = clock_->ApproximateNow();
55
56  // First detection parameter - ack-train detection.
57  // Since slow start burst out packets we can indirectly estimate the inter-
58  // arrival time by looking at the arrival time of the ACKs if the ACKs are
59  // spread out more then half the minimum RTT packets are beeing spread out
60  // more than the capacity.
61  // This first trigger will not come into play until we hit roughly 4.8 Mbit/s.
62  // TODO(pwestin): we need to make sure our pacing don't trigger this detector.
63  if (current_time.Subtract(last_time_).ToMicroseconds() <=
64      kHybridStartDelayMinThresholdUs) {
65    last_time_ = current_time;
66    if (current_time.Subtract(round_start_).ToMicroseconds() >=
67        (delay_min.ToMicroseconds() >> 1)) {
68      found_ack_train_ = true;
69    }
70  }
71  // Second detection parameter - delay increase detection.
72  // Compare the minimum delay (current_rtt_) of the current
73  // burst of packets relative to the minimum delay during the session.
74  // Note: we only look at the first few(8) packets in each burst, since we
75  // only want to compare the lowest RTT of the burst relative to previous
76  // bursts.
77  sample_count_++;
78  if (sample_count_ <= kHybridStartMinSamples) {
79    if (current_rtt_.IsZero() || current_rtt_ > rtt) {
80      current_rtt_ = rtt;
81    }
82  }
83  // We only need to check this once.
84  if (sample_count_ == kHybridStartMinSamples) {
85    int accepted_variance_us = delay_min.ToMicroseconds() >>
86        kHybridStartDelayFactorExp;
87    accepted_variance_us = std::min(accepted_variance_us,
88                                    kHybridStartDelayMaxThresholdUs);
89    QuicTime::Delta accepted_variance = QuicTime::Delta::FromMicroseconds(
90        std::max(accepted_variance_us, kHybridStartDelayMinThresholdUs));
91
92    if (current_rtt_ > delay_min.Add(accepted_variance)) {
93      found_delay_ = true;
94    }
95  }
96}
97
98bool HybridSlowStart::Exit() {
99  // If either one of the two conditions are met we exit from slow start
100  // immediately.
101  if (found_ack_train_ || found_delay_) {
102    return true;
103  }
104  return false;
105}
106
107QuicTime::Delta HybridSlowStart::SmoothedRtt() {
108  // TODO(satyamshekhar): Calculate and return smooth average of rtt over time.
109  return current_rtt_;
110}
111
112}  // namespace net
113