mirror of
https://github.com/facebookincubator/mvfst.git
synced 2025-07-29 03:41:11 +03:00
Refactor AckScheduler to use it in both PacketScheduler and PacketRebuilder
Summary: The logic for deciding which ACK type to write was duplicated in QuicPacketScheduler and QuicPacketRebuilder. This refactors the logic out into a separate QuicAckScheduler so it can be tested for correction and reused in both places. Reviewed By: sharmafb Differential Revision: D69933311 fbshipit-source-id: e4f45688a5d258dd2a57f9f7844407f3efad5f49
This commit is contained in:
committed by
Facebook GitHub Bot
parent
aac108ddc7
commit
c6d8f76e67
@ -113,25 +113,6 @@ class MiddleStartingIterationWrapper {
|
||||
|
||||
namespace quic {
|
||||
|
||||
bool hasAcksToSchedule(const AckState& ackState) {
|
||||
Optional<PacketNum> largestAckSend = largestAckToSend(ackState);
|
||||
if (!largestAckSend) {
|
||||
return false;
|
||||
}
|
||||
if (!ackState.largestAckScheduled) {
|
||||
// Never scheduled an ack, we need to send
|
||||
return true;
|
||||
}
|
||||
return *largestAckSend > *(ackState.largestAckScheduled);
|
||||
}
|
||||
|
||||
Optional<PacketNum> largestAckToSend(const AckState& ackState) {
|
||||
if (ackState.acks.empty()) {
|
||||
return none;
|
||||
}
|
||||
return ackState.acks.back().end;
|
||||
}
|
||||
|
||||
// Schedulers
|
||||
|
||||
FrameScheduler::Builder::Builder(
|
||||
@ -569,108 +550,6 @@ bool StreamFrameScheduler::writeStreamFrame(
|
||||
return true;
|
||||
}
|
||||
|
||||
AckScheduler::AckScheduler(
|
||||
const QuicConnectionStateBase& conn,
|
||||
const AckState& ackState)
|
||||
: conn_(conn), ackState_(ackState) {}
|
||||
|
||||
Optional<PacketNum> AckScheduler::writeNextAcks(
|
||||
PacketBuilderInterface& builder) {
|
||||
// Use default ack delay for long headers. Usually long headers are sent
|
||||
// before crypto negotiation, so the peer might not know about the ack delay
|
||||
// exponent yet, so we use the default.
|
||||
uint8_t ackDelayExponentToUse =
|
||||
builder.getPacketHeader().getHeaderForm() == HeaderForm::Long
|
||||
? kDefaultAckDelayExponent
|
||||
: conn_.transportSettings.ackDelayExponent;
|
||||
auto largestAckedPacketNum = *largestAckToSend(ackState_);
|
||||
auto ackingTime = Clock::now();
|
||||
DCHECK(ackState_.largestRecvdPacketTime.hasValue())
|
||||
<< "Missing received time for the largest acked packet";
|
||||
// assuming that we're going to ack the largest received with highest pri
|
||||
auto receivedTime = *ackState_.largestRecvdPacketTime;
|
||||
std::chrono::microseconds ackDelay =
|
||||
(ackingTime > receivedTime
|
||||
? std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
ackingTime - receivedTime)
|
||||
: 0us);
|
||||
|
||||
WriteAckFrameMetaData meta = {
|
||||
ackState_, /* ackState*/
|
||||
ackDelay, /* ackDelay */
|
||||
static_cast<uint8_t>(ackDelayExponentToUse), /* ackDelayExponent */
|
||||
conn_.connectionTime, /* connect timestamp */
|
||||
};
|
||||
|
||||
Optional<WriteAckFrameResult> ackWriteResult;
|
||||
|
||||
bool isAckReceiveTimestampsSupported =
|
||||
conn_.transportSettings.maybeAckReceiveTimestampsConfigSentToPeer &&
|
||||
conn_.maybePeerAckReceiveTimestampsConfig;
|
||||
|
||||
uint64_t peerRequestedTimestampsCount =
|
||||
conn_.maybePeerAckReceiveTimestampsConfig.has_value()
|
||||
? conn_.maybePeerAckReceiveTimestampsConfig.value()
|
||||
.maxReceiveTimestampsPerAck
|
||||
: 0;
|
||||
|
||||
uint64_t extendedAckSupportedAndEnabled =
|
||||
conn_.peerAdvertisedExtendedAckFeatures &
|
||||
conn_.transportSettings.enableExtendedAckFeatures;
|
||||
// Disable the ECN fields if we are not reading them
|
||||
if (!conn_.transportSettings.readEcnOnIngress) {
|
||||
extendedAckSupportedAndEnabled &= ~static_cast<ExtendedAckFeatureMaskType>(
|
||||
ExtendedAckFeatureMask::ECN_COUNTS);
|
||||
}
|
||||
// Disable the receive timestamps fields if we have not regoatiated receive
|
||||
// timestamps support
|
||||
if (!isAckReceiveTimestampsSupported || (peerRequestedTimestampsCount == 0)) {
|
||||
extendedAckSupportedAndEnabled &= ~static_cast<ExtendedAckFeatureMaskType>(
|
||||
ExtendedAckFeatureMask::RECEIVE_TIMESTAMPS);
|
||||
}
|
||||
|
||||
if (extendedAckSupportedAndEnabled > 0) {
|
||||
// The peer supports extended ACKs and we have them enabled.
|
||||
ackWriteResult = writeAckFrame(
|
||||
meta,
|
||||
builder,
|
||||
FrameType::ACK_EXTENDED,
|
||||
conn_.transportSettings.maybeAckReceiveTimestampsConfigSentToPeer
|
||||
.value_or(AckReceiveTimestampsConfig()),
|
||||
peerRequestedTimestampsCount,
|
||||
extendedAckSupportedAndEnabled);
|
||||
} else if (
|
||||
conn_.transportSettings.readEcnOnIngress &&
|
||||
(meta.ackState.ecnECT0CountReceived ||
|
||||
meta.ackState.ecnECT1CountReceived ||
|
||||
meta.ackState.ecnCECountReceived)) {
|
||||
// We have to report ECN counts, but we can't use the extended ACK frame. In
|
||||
// this case, we give ACK_ECN precedence over ACK_RECEIVE_TIMESTAMPS.
|
||||
ackWriteResult = writeAckFrame(meta, builder, FrameType::ACK_ECN);
|
||||
} else if (
|
||||
isAckReceiveTimestampsSupported && (peerRequestedTimestampsCount > 0)) {
|
||||
// Use ACK_RECEIVE_TIMESTAMPS if its enabled on both endpoints AND the peer
|
||||
// requests at least 1 timestamp
|
||||
ackWriteResult = writeAckFrame(
|
||||
meta,
|
||||
builder,
|
||||
FrameType::ACK_RECEIVE_TIMESTAMPS,
|
||||
conn_.transportSettings.maybeAckReceiveTimestampsConfigSentToPeer
|
||||
.value(),
|
||||
peerRequestedTimestampsCount);
|
||||
} else {
|
||||
ackWriteResult = writeAckFrame(meta, builder, FrameType::ACK);
|
||||
}
|
||||
if (!ackWriteResult) {
|
||||
return none;
|
||||
}
|
||||
return largestAckedPacketNum;
|
||||
}
|
||||
|
||||
bool AckScheduler::hasPendingAcks() const {
|
||||
return hasAcksToSchedule(ackState_);
|
||||
}
|
||||
|
||||
RstStreamScheduler::RstStreamScheduler(const QuicConnectionStateBase& conn)
|
||||
: conn_(conn) {}
|
||||
|
||||
|
Reference in New Issue
Block a user