blob: 25e2a8afaf3b62561d7f4bad79c3fb63c4ff818a [file] [log] [blame]
// Copyright 2014 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_unacked_packet_map.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "net/quic/quic_connection_stats.h"
#include "net/quic/quic_utils_chromium.h"
using std::max;
namespace net {
QuicUnackedPacketMap::QuicUnackedPacketMap()
: largest_sent_packet_(0),
largest_observed_(0),
least_unacked_(1),
bytes_in_flight_(0),
pending_crypto_packet_count_(0) {
}
QuicUnackedPacketMap::~QuicUnackedPacketMap() {
QuicPacketSequenceNumber index = least_unacked_;
for (UnackedPacketMap::iterator it = unacked_packets_.begin();
it != unacked_packets_.end(); ++it, ++index) {
delete it->retransmittable_frames;
// Only delete all_transmissions once, for the newest packet.
if (it->all_transmissions != nullptr &&
index == *it->all_transmissions->rbegin()) {
delete it->all_transmissions;
}
}
}
void QuicUnackedPacketMap::AddSentPacket(
const SerializedPacket& packet,
QuicPacketSequenceNumber old_sequence_number,
TransmissionType transmission_type,
QuicTime sent_time,
QuicByteCount bytes_sent,
bool set_in_flight) {
QuicPacketSequenceNumber sequence_number = packet.sequence_number;
LOG_IF(DFATAL, largest_sent_packet_ > sequence_number);
DCHECK_GE(sequence_number, least_unacked_ + unacked_packets_.size());
while (least_unacked_ + unacked_packets_.size() < sequence_number) {
unacked_packets_.push_back(TransmissionInfo());
unacked_packets_.back().is_unackable = true;
}
TransmissionInfo info(packet.retransmittable_frames,
packet.sequence_number_length,
transmission_type,
sent_time);
info.is_fec_packet = packet.is_fec_packet;
if (old_sequence_number == 0) {
if (packet.retransmittable_frames != nullptr &&
packet.retransmittable_frames->HasCryptoHandshake() == IS_HANDSHAKE) {
++pending_crypto_packet_count_;
}
} else {
TransferRetransmissionInfo(
old_sequence_number, sequence_number, transmission_type, &info);
}
largest_sent_packet_ = sequence_number;
if (set_in_flight) {
bytes_in_flight_ += bytes_sent;
info.bytes_sent = bytes_sent;
info.in_flight = true;
}
unacked_packets_.push_back(info);
}
void QuicUnackedPacketMap::RemoveObsoletePackets() {
while (!unacked_packets_.empty()) {
if (!IsPacketRemovable(least_unacked_, unacked_packets_.front())) {
break;
}
unacked_packets_.pop_front();
++least_unacked_;
}
}
void QuicUnackedPacketMap::TransferRetransmissionInfo(
QuicPacketSequenceNumber old_sequence_number,
QuicPacketSequenceNumber new_sequence_number,
TransmissionType transmission_type,
TransmissionInfo* info) {
DCHECK_GE(old_sequence_number, least_unacked_);
DCHECK_LT(old_sequence_number, least_unacked_ + unacked_packets_.size());
DCHECK_GE(new_sequence_number, least_unacked_ + unacked_packets_.size());
DCHECK_NE(NOT_RETRANSMISSION, transmission_type);
// TODO(ianswett): Discard and lose the packet lazily instead of immediately.
TransmissionInfo* transmission_info =
&unacked_packets_.at(old_sequence_number - least_unacked_);
RetransmittableFrames* frames = transmission_info->retransmittable_frames;
transmission_info->retransmittable_frames = nullptr;
LOG_IF(DFATAL, frames == nullptr)
<< "Attempt to retransmit packet with no "
<< "retransmittable frames: " << old_sequence_number;
// Only keep one transmission older than largest observed, because only the
// most recent is expected to possibly be a spurious retransmission.
while (transmission_info->all_transmissions != nullptr &&
transmission_info->all_transmissions->size() > 1 &&
*(++transmission_info->all_transmissions->begin()) <
largest_observed_) {
QuicPacketSequenceNumber old_transmission =
*transmission_info->all_transmissions->begin();
TransmissionInfo* old_info =
&unacked_packets_[old_transmission - least_unacked_];
// Don't remove old packets if they're still in flight.
if (old_info->in_flight) {
break;
}
old_info->all_transmissions->pop_front();
// This will cause the packet be removed in RemoveObsoletePackets.
old_info->all_transmissions = nullptr;
}
// Don't link old transmissions to new ones when version or
// encryption changes.
if (transmission_type == ALL_INITIAL_RETRANSMISSION ||
transmission_type == ALL_UNACKED_RETRANSMISSION) {
RemoveAckability(transmission_info);
} else {
if (transmission_info->all_transmissions == nullptr) {
transmission_info->all_transmissions = new SequenceNumberList();
transmission_info->all_transmissions->push_back(old_sequence_number);
}
transmission_info->all_transmissions->push_back(new_sequence_number);
}
info->retransmittable_frames = frames;
info->all_transmissions = transmission_info->all_transmissions;
// Proactively remove obsolete packets so the least unacked can be raised.
RemoveObsoletePackets();
}
void QuicUnackedPacketMap::ClearAllPreviousRetransmissions() {
while (!unacked_packets_.empty() && least_unacked_ < largest_observed_) {
// If this packet is in flight, or has retransmittable data, then there is
// no point in clearing out any further packets, because they would not
// affect the high water mark.
TransmissionInfo* info = &unacked_packets_.front();
if (info->in_flight || info->retransmittable_frames != nullptr) {
break;
}
if (info->all_transmissions != nullptr) {
if (info->all_transmissions->size() < 2) {
LOG(DFATAL) << "all_transmissions must be nullptr or have multiple "
<< "elements. size:" << info->all_transmissions->size();
delete info->all_transmissions;
} else {
info->all_transmissions->pop_front();
if (info->all_transmissions->size() == 1) {
// Set the newer transmission's 'all_transmissions' entry to nullptr.
QuicPacketSequenceNumber new_transmission =
info->all_transmissions->front();
TransmissionInfo* new_info =
&unacked_packets_.at(new_transmission - least_unacked_);
delete new_info->all_transmissions;
new_info->all_transmissions = nullptr;
}
}
}
unacked_packets_.pop_front();
++least_unacked_;
}
}
bool QuicUnackedPacketMap::HasRetransmittableFrames(
QuicPacketSequenceNumber sequence_number) const {
DCHECK_GE(sequence_number, least_unacked_);
DCHECK_LT(sequence_number, least_unacked_ + unacked_packets_.size());
return unacked_packets_[sequence_number - least_unacked_]
.retransmittable_frames != nullptr;
}
void QuicUnackedPacketMap::NackPacket(QuicPacketSequenceNumber sequence_number,
QuicPacketCount min_nacks) {
DCHECK_GE(sequence_number, least_unacked_);
DCHECK_LT(sequence_number, least_unacked_ + unacked_packets_.size());
unacked_packets_[sequence_number - least_unacked_].nack_count =
max(min_nacks,
unacked_packets_[sequence_number - least_unacked_].nack_count);
}
void QuicUnackedPacketMap::RemoveRetransmittability(
QuicPacketSequenceNumber sequence_number) {
DCHECK_GE(sequence_number, least_unacked_);
DCHECK_LT(sequence_number, least_unacked_ + unacked_packets_.size());
TransmissionInfo* info = &unacked_packets_[sequence_number - least_unacked_];
SequenceNumberList* all_transmissions = info->all_transmissions;
if (all_transmissions == nullptr) {
MaybeRemoveRetransmittableFrames(info);
return;
}
// TODO(ianswett): Consider adding a check to ensure there are retransmittable
// frames associated with this packet.
for (SequenceNumberList::const_iterator it = all_transmissions->begin();
it != all_transmissions->end(); ++it) {
TransmissionInfo* transmission_info =
&unacked_packets_[*it - least_unacked_];
MaybeRemoveRetransmittableFrames(transmission_info);
transmission_info->all_transmissions = nullptr;
}
delete all_transmissions;
}
void QuicUnackedPacketMap::RemoveAckability(TransmissionInfo* info) {
DCHECK(info->retransmittable_frames == nullptr);
info->is_unackable = true;
SequenceNumberList* all_transmissions = info->all_transmissions;
if (all_transmissions == nullptr) {
return;
}
for (SequenceNumberList::const_iterator it = all_transmissions->begin();
it != all_transmissions->end(); ++it) {
TransmissionInfo* transmission_info =
&unacked_packets_[*it - least_unacked_];
transmission_info->all_transmissions = nullptr;
transmission_info->is_unackable = true;
}
delete all_transmissions;
}
void QuicUnackedPacketMap::MaybeRemoveRetransmittableFrames(
TransmissionInfo* transmission_info) {
if (transmission_info->retransmittable_frames != nullptr) {
if (transmission_info->retransmittable_frames->HasCryptoHandshake()
== IS_HANDSHAKE) {
--pending_crypto_packet_count_;
}
delete transmission_info->retransmittable_frames;
transmission_info->retransmittable_frames = nullptr;
}
}
void QuicUnackedPacketMap::IncreaseLargestObserved(
QuicPacketSequenceNumber largest_observed) {
DCHECK_LE(largest_observed_, largest_observed);
largest_observed_ = largest_observed;
}
bool QuicUnackedPacketMap::IsPacketUsefulForMeasuringRtt(
QuicPacketSequenceNumber sequence_number,
const TransmissionInfo& info) const {
// Packet can be used for RTT measurement if it may yet be acked as the
// largest observed packet by the receiver.
return !info.is_unackable && sequence_number > largest_observed_;
}
bool QuicUnackedPacketMap::IsPacketUsefulForCongestionControl(
QuicPacketSequenceNumber sequence_number,
const TransmissionInfo& info) const {
// Packet contributes to congestion control if it is considered inflight.
return info.in_flight;
}
bool QuicUnackedPacketMap::IsPacketUsefulForRetransmittableData(
QuicPacketSequenceNumber sequence_number,
const TransmissionInfo& info) const {
// Packet may have retransmittable frames, or the data may have been
// retransmitted with a new sequence number.
return info.retransmittable_frames != nullptr ||
info.all_transmissions != nullptr;
}
bool QuicUnackedPacketMap::IsPacketUseless(
QuicPacketSequenceNumber sequence_number,
const TransmissionInfo& info) const {
return !IsPacketUsefulForMeasuringRtt(sequence_number, info) &&
!IsPacketUsefulForCongestionControl(sequence_number, info) &&
!IsPacketUsefulForRetransmittableData(sequence_number, info);
}
bool QuicUnackedPacketMap::IsPacketRemovable(
QuicPacketSequenceNumber sequence_number,
const TransmissionInfo& info) const {
return (!IsPacketUsefulForMeasuringRtt(sequence_number, info) ||
unacked_packets_.size() > kMaxTcpCongestionWindow) &&
!IsPacketUsefulForCongestionControl(sequence_number, info) &&
!IsPacketUsefulForRetransmittableData(sequence_number, info);
}
bool QuicUnackedPacketMap::IsUnacked(
QuicPacketSequenceNumber sequence_number) const {
if (sequence_number < least_unacked_ ||
sequence_number >= least_unacked_ + unacked_packets_.size()) {
return false;
}
return !IsPacketUseless(sequence_number,
unacked_packets_[sequence_number - least_unacked_]);
}
void QuicUnackedPacketMap::RemoveFromInFlight(
QuicPacketSequenceNumber sequence_number) {
DCHECK_GE(sequence_number, least_unacked_);
DCHECK_LT(sequence_number, least_unacked_ + unacked_packets_.size());
TransmissionInfo* info = &unacked_packets_[sequence_number - least_unacked_];
if (info->in_flight) {
LOG_IF(DFATAL, bytes_in_flight_ < info->bytes_sent);
bytes_in_flight_ -= info->bytes_sent;
info->in_flight = false;
}
}
bool QuicUnackedPacketMap::HasUnackedPackets() const {
return !unacked_packets_.empty();
}
bool QuicUnackedPacketMap::HasInFlightPackets() const {
return bytes_in_flight_ > 0;
}
const TransmissionInfo& QuicUnackedPacketMap::GetTransmissionInfo(
QuicPacketSequenceNumber sequence_number) const {
return unacked_packets_[sequence_number - least_unacked_];
}
QuicTime QuicUnackedPacketMap::GetLastPacketSentTime() const {
UnackedPacketMap::const_reverse_iterator it = unacked_packets_.rbegin();
while (it != unacked_packets_.rend()) {
if (it->in_flight) {
LOG_IF(DFATAL, it->sent_time == QuicTime::Zero())
<< "Sent time can never be zero for a packet in flight.";
return it->sent_time;
}
++it;
}
LOG(DFATAL) << "GetLastPacketSentTime requires in flight packets.";
return QuicTime::Zero();
}
QuicTime QuicUnackedPacketMap::GetFirstInFlightPacketSentTime() const {
UnackedPacketMap::const_iterator it = unacked_packets_.begin();
while (it != unacked_packets_.end() && !it->in_flight) {
++it;
}
if (it == unacked_packets_.end()) {
LOG(DFATAL) << "GetFirstInFlightPacketSentTime requires in flight packets.";
return QuicTime::Zero();
}
return it->sent_time;
}
size_t QuicUnackedPacketMap::GetNumUnackedPacketsDebugOnly() const {
size_t unacked_packet_count = 0;
QuicPacketSequenceNumber sequence_number = least_unacked_;
for (UnackedPacketMap::const_iterator it = unacked_packets_.begin();
it != unacked_packets_.end(); ++it, ++sequence_number) {
if (!IsPacketUseless(sequence_number, *it)) {
++unacked_packet_count;
}
}
return unacked_packet_count;
}
bool QuicUnackedPacketMap::HasMultipleInFlightPackets() const {
size_t num_in_flight = 0;
for (UnackedPacketMap::const_reverse_iterator it = unacked_packets_.rbegin();
it != unacked_packets_.rend(); ++it) {
if (it->in_flight) {
++num_in_flight;
}
if (num_in_flight > 1) {
return true;
}
}
return false;
}
bool QuicUnackedPacketMap::HasPendingCryptoPackets() const {
return pending_crypto_packet_count_ > 0;
}
bool QuicUnackedPacketMap::HasUnackedRetransmittableFrames() const {
for (UnackedPacketMap::const_reverse_iterator it =
unacked_packets_.rbegin(); it != unacked_packets_.rend(); ++it) {
if (it->in_flight && it->retransmittable_frames) {
return true;
}
}
return false;
}
QuicPacketSequenceNumber
QuicUnackedPacketMap::GetLeastUnacked() const {
return least_unacked_;
}
void QuicUnackedPacketMap::RestoreInFlight(
QuicPacketSequenceNumber sequence_number) {
DCHECK_GE(sequence_number, least_unacked_);
DCHECK_LT(sequence_number, least_unacked_ + unacked_packets_.size());
TransmissionInfo* info = &unacked_packets_[sequence_number - least_unacked_];
DCHECK(!info->in_flight);
DCHECK_NE(0u, info->bytes_sent);
DCHECK(info->sent_time.IsInitialized());
bytes_in_flight_ += info->bytes_sent;
info->in_flight = true;
}
} // namespace net