blob: 7095e60306b858333f63004160c35c531ad01fba [file] [log] [blame]
// 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