// 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