1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-11-27 03:41:14 +03:00
Files
mvfst/quic/loss/QuicLossFunctions.cpp
Subodh Iyengar 04baa15a04 Custom variant type for packetheader
Summary:
Make a custom variant type for PacketHeader. By not relying on boost::variant
this reduces the code size of the implementation.

This uses a combination of a union type as well as a enum type to emulate a variant

Reviewed By: yangchi

Differential Revision: D17187589

fbshipit-source-id: 00c2b9b8dd3f3e73af766d84888b13b9d867165a
2019-09-19 17:31:47 -07:00

195 lines
6.9 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
#include "quic/loss/QuicLossFunctions.h"
namespace quic {
std::chrono::microseconds calculatePTO(const QuicConnectionStateBase& conn) {
return conn.lossState.srtt + 4 * conn.lossState.rttvar +
conn.lossState.maxAckDelay;
}
bool isPersistentCongestion(
const QuicConnectionStateBase& conn,
TimePoint lostPeriodStart,
TimePoint lostPeriodEnd) noexcept {
if (conn.lossState.srtt == 0us) {
return false;
}
auto pto = calculatePTO(conn);
return (lostPeriodEnd - lostPeriodStart) >=
pto * kPersistentCongestionThreshold;
}
void onPTOAlarm(QuicConnectionStateBase& conn) {
VLOG(10) << __func__ << " " << conn;
QUIC_TRACE(
pto_alarm,
conn,
conn.lossState.largestSent,
conn.lossState.ptoCount,
(uint64_t)conn.outstandingPackets.size());
QUIC_STATS(conn.infoCallback, onPTO);
conn.lossState.ptoCount++;
conn.lossState.totalPTOCount++;
if (conn.qLogger) {
conn.qLogger->addLossAlarm(
conn.lossState.largestSent,
conn.lossState.ptoCount,
(uint64_t)conn.outstandingPackets.size(),
kPtoAlarm);
}
if (conn.lossState.ptoCount == conn.transportSettings.maxNumPTOs) {
throw QuicInternalException("Exceeded max PTO", LocalErrorCode::NO_ERROR);
}
conn.pendingEvents.numProbePackets = kPacketToSendForPTO;
}
void markPacketLoss(
QuicConnectionStateBase& conn,
RegularQuicWritePacket& packet,
bool processed,
PacketNum currentPacketNum) {
for (auto& packetFrame : packet.frames) {
folly::variant_match(
packetFrame,
[&](MaxStreamDataFrame& frame) {
// For all other frames, we process it if it's not from a clone
// packet, or if the clone and its siblings have never been processed.
// But for both MaxData and MaxStreamData, clone and its siblings may
// have different values. So we process it if it matches the
// latestMaxDataPacket or latestMaxStreamDataPacket. If an older
// packet also has such frames, it's ok to skip process of such loss
// since newer value is already sent in later packets.
auto stream = conn.streamManager->getStream(frame.streamId);
if (!stream) {
return;
}
// TODO: check for the stream is in Open or HalfClosedLocal state, the
// peer doesn't need a flow control update in these cases.
if (stream->latestMaxStreamDataPacket == currentPacketNum) {
onStreamWindowUpdateLost(*stream);
}
},
[&](MaxDataFrame&) {
if (conn.latestMaxDataPacket == currentPacketNum) {
onConnWindowUpdateLost(conn);
}
},
// For other frame types, we only process them if the packet is not a
// processed clone.
[&](WriteStreamFrame& frame) {
if (processed) {
return;
}
auto stream = conn.streamManager->getStream(frame.streamId);
if (!stream) {
return;
}
auto bufferItr = std::lower_bound(
stream->retransmissionBuffer.begin(),
stream->retransmissionBuffer.end(),
frame.offset,
[](const auto& buffer, const auto& offset) {
return buffer.offset < offset;
});
if (bufferItr == stream->retransmissionBuffer.end()) {
// It's possible that the stream was reset or data on the stream was
// skipped while we discovered that its packet was lost so we might
// not have the offset.
return;
}
// The original rxmt offset might have been bumped up after it was
// shrunk due to egress partially reliable skip.
if (!ackFrameMatchesRetransmitBuffer(*stream, frame, *bufferItr)) {
return;
}
stream->lossBuffer.insert(
std::upper_bound(
stream->lossBuffer.begin(),
stream->lossBuffer.end(),
bufferItr->offset,
[](const auto& offset, const auto& buffer) {
return offset < buffer.offset;
}),
std::move(*bufferItr));
stream->retransmissionBuffer.erase(bufferItr);
conn.streamManager->updateLossStreams(*stream);
},
[&](WriteCryptoFrame& frame) {
if (processed) {
return;
}
auto protectionType = packet.header.getProtectionType();
auto encryptionLevel =
protectionTypeToEncryptionLevel(protectionType);
auto cryptoStream =
getCryptoStream(*conn.cryptoState, encryptionLevel);
auto bufferItr = std::lower_bound(
cryptoStream->retransmissionBuffer.begin(),
cryptoStream->retransmissionBuffer.end(),
frame.offset,
[](const auto& buffer, const auto& offset) {
return buffer.offset < offset;
});
if (bufferItr == cryptoStream->retransmissionBuffer.end()) {
// It's possible that the stream was reset while we discovered that
// it's packet was lost so we might not have the offset.
return;
}
DCHECK_EQ(bufferItr->offset, frame.offset);
cryptoStream->lossBuffer.insert(
std::upper_bound(
cryptoStream->lossBuffer.begin(),
cryptoStream->lossBuffer.end(),
bufferItr->offset,
[](const auto& offset, const auto& buffer) {
return offset < buffer.offset;
}),
std::move(*bufferItr));
cryptoStream->retransmissionBuffer.erase(bufferItr);
},
[&](RstStreamFrame& frame) {
if (processed) {
return;
}
auto stream = conn.streamManager->getStream(frame.streamId);
if (!stream) {
// If the stream is dead, ignore the retransmissions of the rst
// stream.
return;
}
// Add the lost RstStreamFrame back to pendingEvents:
conn.pendingEvents.resets.insert({frame.streamId, frame});
},
[&](StreamDataBlockedFrame& frame) {
if (processed) {
return;
}
auto stream = conn.streamManager->getStream(frame.streamId);
// TODO: check for retransmittable
if (!stream) {
return;
}
onBlockedLost(*stream);
},
[&](QuicSimpleFrame& frame) {
if (processed) {
return;
}
updateSimpleFrameOnPacketLoss(conn, frame);
},
[&](auto&) {
// ignore the rest of the frames.
});
}
}
} // namespace quic