using std::min;
namespace net {
+
+// The length of the recent min rtt window in seconds. Windowing is disabled for
+// values less than or equal to 0.
+int32 FLAGS_quic_recent_min_rtt_window_s = 60;
+
namespace {
static const int kDefaultRetransmissionTimeMs = 500;
// TCP RFC calls for 1 second RTO however Linux differs from this default and
// Number of samples before we force a new recent min rtt to be captured.
static const size_t kNumMinRttSamplesAfterQuiescence = 2;
+// Number of unpaced packets to send after quiescence.
+static const size_t kInitialUnpacedBurst = 10;
+
bool HasCryptoHandshake(const TransmissionInfo& transmission_info) {
if (transmission_info.retransmittable_frames == NULL) {
return false;
#define ENDPOINT (is_server_ ? "Server: " : " Client: ")
-QuicSentPacketManager::QuicSentPacketManager(bool is_server,
- const QuicClock* clock,
- QuicConnectionStats* stats,
- CongestionFeedbackType type,
- LossDetectionType loss_type)
+QuicSentPacketManager::QuicSentPacketManager(
+ bool is_server,
+ const QuicClock* clock,
+ QuicConnectionStats* stats,
+ CongestionControlType congestion_control_type,
+ LossDetectionType loss_type)
: unacked_packets_(),
is_server_(is_server),
clock_(clock),
stats_(stats),
debug_delegate_(NULL),
- send_algorithm_(
- SendAlgorithmInterface::Create(clock, &rtt_stats_, type, stats)),
+ network_change_visitor_(NULL),
+ send_algorithm_(SendAlgorithmInterface::Create(clock,
+ &rtt_stats_,
+ congestion_control_type,
+ stats)),
loss_algorithm_(LossDetectionInterface::Create(loss_type)),
largest_observed_(0),
+ first_rto_transmission_(0),
consecutive_rto_count_(0),
consecutive_tlp_count_(0),
consecutive_crypto_retransmission_count_(0),
pending_tlp_transmission_(false),
max_tail_loss_probes_(kDefaultMaxTailLossProbes),
- using_pacing_(false) {
+ using_pacing_(false),
+ handshake_confirmed_(false) {
}
QuicSentPacketManager::~QuicSentPacketManager() {
config.ReceivedInitialRoundTripTimeUs()));
}
// TODO(ianswett): BBR is currently a server only feature.
- if (config.HasReceivedCongestionOptions() &&
- ContainsQuicTag(config.ReceivedCongestionOptions(), kTBBR)) {
+ if (config.HasReceivedConnectionOptions() &&
+ ContainsQuicTag(config.ReceivedConnectionOptions(), kTBBR)) {
+ if (FLAGS_quic_recent_min_rtt_window_s > 0) {
+ rtt_stats_.set_recent_min_rtt_window(
+ QuicTime::Delta::FromSeconds(FLAGS_quic_recent_min_rtt_window_s));
+ }
send_algorithm_.reset(
- SendAlgorithmInterface::Create(clock_, &rtt_stats_, kTCPBBR, stats_));
+ SendAlgorithmInterface::Create(clock_, &rtt_stats_, kBBR, stats_));
}
- if (config.congestion_feedback() == kPACE) {
+ if (config.HasReceivedConnectionOptions() &&
+ ContainsQuicTag(config.ReceivedConnectionOptions(), kRENO)) {
+ send_algorithm_.reset(
+ SendAlgorithmInterface::Create(clock_, &rtt_stats_, kReno, stats_));
+ }
+ if (config.HasReceivedConnectionOptions() &&
+ ContainsQuicTag(config.ReceivedConnectionOptions(), kPACE)) {
MaybeEnablePacing();
}
- if (config.HasReceivedLossDetection() &&
- config.ReceivedLossDetection() == kTIME) {
+ // TODO(ianswett): Remove the "HasReceivedLossDetection" branch once
+ // the ConnectionOptions code is live everywhere.
+ if ((config.HasReceivedLossDetection() &&
+ config.ReceivedLossDetection() == kTIME) ||
+ (config.HasReceivedConnectionOptions() &&
+ ContainsQuicTag(config.ReceivedConnectionOptions(), kTIME))) {
loss_algorithm_.reset(LossDetectionInterface::Create(kTime));
}
send_algorithm_->SetFromConfig(config, is_server_);
+
+ if (network_change_visitor_ != NULL) {
+ network_change_visitor_->OnCongestionWindowChange(GetCongestionWindow());
+ }
}
// TODO(ianswett): Combine this method with OnPacketSent once packets are always
unacked_packets_.OnRetransmittedPacket(old_sequence_number,
new_sequence_number,
transmission_type);
+
+ if (debug_delegate_ != NULL) {
+ debug_delegate_->OnRetransmittedPacket(old_sequence_number,
+ new_sequence_number,
+ transmission_type,
+ clock_->ApproximateNow());
+ }
}
-void QuicSentPacketManager::OnIncomingAck(
- const ReceivedPacketInfo& received_info,
- QuicTime ack_receive_time) {
+void QuicSentPacketManager::OnIncomingAck(const QuicAckFrame& ack_frame,
+ QuicTime ack_receive_time) {
QuicByteCount bytes_in_flight = unacked_packets_.bytes_in_flight();
// We rely on delta_time_largest_observed to compute an RTT estimate, so
// we only update rtt when the largest observed gets acked.
- bool largest_observed_acked = MaybeUpdateRTT(received_info, ack_receive_time);
- if (largest_observed_ < received_info.largest_observed) {
- largest_observed_ = received_info.largest_observed;
+ bool largest_observed_acked = MaybeUpdateRTT(ack_frame, ack_receive_time);
+ if (largest_observed_ < ack_frame.largest_observed) {
+ largest_observed_ = ack_frame.largest_observed;
unacked_packets_.IncreaseLargestObserved(largest_observed_);
}
- HandleAckForSentPackets(received_info);
+ HandleAckForSentPackets(ack_frame);
InvokeLossDetection(ack_receive_time);
MaybeInvokeCongestionEvent(largest_observed_acked, bytes_in_flight);
// If we have received a truncated ack, then we need to clear out some
// previous transmissions to allow the peer to actually ACK new packets.
- if (received_info.is_truncated) {
+ if (ack_frame.is_truncated) {
unacked_packets_.ClearPreviousRetransmissions(
- received_info.missing_packets.size() / 2);
+ ack_frame.missing_packets.size() / 2);
}
// Anytime we are making forward progress and have a new RTT estimate, reset
consecutive_tlp_count_ = 0;
consecutive_crypto_retransmission_count_ = 0;
}
+
+ if (debug_delegate_ != NULL) {
+ debug_delegate_->OnIncomingAck(ack_frame,
+ ack_receive_time,
+ largest_observed_,
+ largest_observed_acked,
+ GetLeastUnackedSentPacket());
+ }
}
void QuicSentPacketManager::MaybeInvokeCongestionEvent(
bool rtt_updated, QuicByteCount bytes_in_flight) {
- if (rtt_updated || !packets_acked_.empty() ||
- !packets_lost_.empty()) {
- send_algorithm_->OnCongestionEvent(
- rtt_updated, bytes_in_flight, packets_acked_, packets_lost_);
- packets_acked_.clear();
- packets_lost_.clear();
+ if (!rtt_updated && packets_acked_.empty() && packets_lost_.empty()) {
+ return;
+ }
+ send_algorithm_->OnCongestionEvent(rtt_updated, bytes_in_flight,
+ packets_acked_, packets_lost_);
+ packets_acked_.clear();
+ packets_lost_.clear();
+ if (network_change_visitor_ != NULL) {
+ network_change_visitor_->OnCongestionWindowChange(GetCongestionWindow());
}
}
void QuicSentPacketManager::HandleAckForSentPackets(
- const ReceivedPacketInfo& received_info) {
+ const QuicAckFrame& ack_frame) {
// Go through the packets we have not received an ack for and see if this
// incoming_ack shows they've been seen by the peer.
QuicTime::Delta delta_largest_observed =
- received_info.delta_time_largest_observed;
+ ack_frame.delta_time_largest_observed;
QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
while (it != unacked_packets_.end()) {
QuicPacketSequenceNumber sequence_number = it->first;
- if (sequence_number > received_info.largest_observed) {
+ if (sequence_number > ack_frame.largest_observed) {
// These packets are still in flight.
break;
}
- if (IsAwaitingPacket(received_info, sequence_number)) {
+ if (IsAwaitingPacket(ack_frame, sequence_number)) {
// Consider it multiple nacks when there is a gap between the missing
// packet and the largest observed, since the purpose of a nack
// threshold is to tolerate re-ordering. This handles both StretchAcks
// and Forward Acks.
// The nack count only increases when the largest observed increases.
- size_t min_nacks = received_info.largest_observed - sequence_number;
+ size_t min_nacks = ack_frame.largest_observed - sequence_number;
// Truncated acks can nack the largest observed, so use a min of 1.
if (min_nacks == 0) {
min_nacks = 1;
// Discard any retransmittable frames associated with revived packets.
for (SequenceNumberSet::const_iterator revived_it =
- received_info.revived_packets.begin();
- revived_it != received_info.revived_packets.end(); ++revived_it) {
+ ack_frame.revived_packets.begin();
+ revived_it != ack_frame.revived_packets.end(); ++revived_it) {
MarkPacketRevived(*revived_it, delta_largest_observed);
}
}
void QuicSentPacketManager::RecordSpuriousRetransmissions(
const SequenceNumberSet& all_transmissions,
QuicPacketSequenceNumber acked_sequence_number) {
+ if (acked_sequence_number < first_rto_transmission_) {
+ // Cancel all pending RTO transmissions and restore their in flight status.
+ // Replace SRTT with latest_rtt and increase the variance to prevent
+ // a spurious RTO from happening again.
+ rtt_stats_.ExpireSmoothedMetrics();
+ for (PendingRetransmissionMap::const_iterator it =
+ pending_retransmissions_.begin();
+ it != pending_retransmissions_.end(); ++it) {
+ DCHECK_EQ(it->second, RTO_RETRANSMISSION);
+ unacked_packets_.RestoreInFlight(it->first);
+ }
+ pending_retransmissions_.clear();
+ send_algorithm_->RevertRetransmissionTimeout();
+ first_rto_transmission_ = 0;
+ ++stats_->spurious_rto_count;
+ }
for (SequenceNumberSet::const_iterator
it = all_transmissions.upper_bound(acked_sequence_number),
end = all_transmissions.end();
has_retransmittable_data);
unacked_packets_.SetSent(sequence_number, sent_time, bytes, in_flight);
+ if (debug_delegate_ != NULL) {
+ debug_delegate_->OnSentPacket(sequence_number, sent_time, bytes);
+ }
+
// Reset the retransmission timer anytime a pending packet is sent.
return in_flight;
}
if (!it->second.in_flight || frames == NULL) {
continue;
}
- DCHECK_NE(IS_HANDSHAKE, frames->HasCryptoHandshake());
+ if (!handshake_confirmed_) {
+ DCHECK_NE(IS_HANDSHAKE, frames->HasCryptoHandshake());
+ }
MarkForRetransmission(sequence_number, TLP_RETRANSMISSION);
return true;
}
send_algorithm_->OnRetransmissionTimeout(packets_retransmitted);
if (packets_retransmitted) {
+ if (consecutive_rto_count_ == 0) {
+ first_rto_transmission_ = unacked_packets_.largest_sent_packet() + 1;
+ }
++consecutive_rto_count_;
}
+
+ if (network_change_visitor_ != NULL) {
+ network_change_visitor_->OnCongestionWindowChange(GetCongestionWindow());
+ }
}
QuicSentPacketManager::RetransmissionTimeoutMode
QuicSentPacketManager::GetRetransmissionMode() const {
DCHECK(unacked_packets_.HasInFlightPackets());
- if (unacked_packets_.HasPendingCryptoPackets()) {
+ if (!handshake_confirmed_ && unacked_packets_.HasPendingCryptoPackets()) {
return HANDSHAKE_MODE;
}
if (loss_algorithm_->GetLossTimeout() != QuicTime::Zero()) {
}
bool QuicSentPacketManager::MaybeUpdateRTT(
- const ReceivedPacketInfo& received_info,
+ const QuicAckFrame& ack_frame,
const QuicTime& ack_receive_time) {
- if (!unacked_packets_.IsUnacked(received_info.largest_observed)) {
+ if (!unacked_packets_.IsUnacked(ack_frame.largest_observed)) {
return false;
}
// We calculate the RTT based on the highest ACKed sequence number, the lower
// sequence numbers will include the ACK aggregation delay.
const TransmissionInfo& transmission_info =
- unacked_packets_.GetTransmissionInfo(received_info.largest_observed);
+ unacked_packets_.GetTransmissionInfo(ack_frame.largest_observed);
// Don't update the RTT if it hasn't been sent.
if (transmission_info.sent_time == QuicTime::Zero()) {
return false;
QuicTime::Delta send_delta =
ack_receive_time.Subtract(transmission_info.sent_time);
rtt_stats_.UpdateRtt(
- send_delta, received_info.delta_time_largest_observed, ack_receive_time);
+ send_delta, ack_frame.delta_time_largest_observed, ack_receive_time);
return true;
}
now, unacked_packets_.bytes_in_flight(), retransmittable);
}
+// Uses a 25ms delayed ack timer. Also helps with better signaling
+// in low-bandwidth (< ~384 kbps), where an ack is sent per packet.
// Ensures that the Delayed Ack timer is always set to a value lesser
// than the retransmission timer's minimum value (MinRTO). We want the
// delayed ack to get back to the QUIC peer before the sender's
// any benefits, but if the delayed ack becomes a significant source
// of (likely, tail) latency, then consider such a mechanism.
const QuicTime::Delta QuicSentPacketManager::DelayedAckTime() const {
- return QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs/2);
+ return QuicTime::Delta::FromMilliseconds(min(kMaxDelayedAckTime,
+ kMinRetransmissionTimeMs/2));
}
const QuicTime QuicSentPacketManager::GetRetransmissionTime() const {
// The RTO is based on the first outstanding packet.
const QuicTime sent_time =
unacked_packets_.GetFirstInFlightPacketSentTime();
- QuicTime rto_timeout = sent_time.Add(GetRetransmissionDelay());
- // Always wait at least 1.5 * RTT from now.
- QuicTime min_timeout = clock_->ApproximateNow().Add(
- rtt_stats_.SmoothedRtt().Multiply(1.5));
-
- return QuicTime::Max(min_timeout, rto_timeout);
+ QuicTime rto_time = sent_time.Add(GetRetransmissionDelay());
+ // Wait for TLP packets to be acked before an RTO fires.
+ QuicTime tlp_time =
+ unacked_packets_.GetLastPacketSentTime().Add(GetTailLossProbeDelay());
+ return QuicTime::Max(tlp_time, rto_time);
}
}
DCHECK(false);
QuicTime::Delta srtt = rtt_stats_.SmoothedRtt();
if (!unacked_packets_.HasMultipleInFlightPackets()) {
return QuicTime::Delta::Max(
- srtt.Multiply(1.5).Add(DelayedAckTime()), srtt.Multiply(2));
+ srtt.Multiply(2), srtt.Multiply(1.5)
+ .Add(QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs/2)));
}
return QuicTime::Delta::FromMilliseconds(
max(kMinTailLossProbeTimeoutMs,
return send_algorithm_->BandwidthEstimate();
}
+bool QuicSentPacketManager::HasReliableBandwidthEstimate() const {
+ return send_algorithm_->HasReliableBandwidthEstimate();
+}
+
QuicByteCount QuicSentPacketManager::GetCongestionWindow() const {
return send_algorithm_->GetCongestionWindow();
}
+QuicByteCount QuicSentPacketManager::GetSlowStartThreshold() const {
+ return send_algorithm_->GetSlowStartThreshold();
+}
+
void QuicSentPacketManager::MaybeEnablePacing() {
if (!FLAGS_enable_quic_pacing) {
return;
using_pacing_ = true;
send_algorithm_.reset(
new PacingSender(send_algorithm_.release(),
- QuicTime::Delta::FromMilliseconds(5)));
+ QuicTime::Delta::FromMilliseconds(5),
+ kInitialUnpacedBurst));
}
} // namespace net