mirror of
https://github.com/facebookincubator/mvfst.git
synced 2025-08-08 09:42:06 +03:00
Implement basic ACK_FREQUENCY support.
Summary: As in title. This doesn't actually send any frames, but implements basic support for the transport parameter and responding to the frames. Reviewed By: yangchi Differential Revision: D26134787 fbshipit-source-id: 2c48e01084034317c8f36f89c69d172e3cb42278
This commit is contained in:
committed by
Facebook GitHub Bot
parent
4f320d8e7b
commit
21f190220e
@@ -161,6 +161,7 @@ enum class FrameType : uint64_t {
|
|||||||
MIN_STREAM_DATA = 0xFE, // subject to change
|
MIN_STREAM_DATA = 0xFE, // subject to change
|
||||||
EXPIRED_STREAM_DATA = 0xFF, // subject to change
|
EXPIRED_STREAM_DATA = 0xFF, // subject to change
|
||||||
KNOB = 0x1550,
|
KNOB = 0x1550,
|
||||||
|
ACK_FREQUENCY = 0xAF,
|
||||||
};
|
};
|
||||||
|
|
||||||
inline constexpr uint16_t toFrameError(FrameType frame) {
|
inline constexpr uint16_t toFrameError(FrameType frame) {
|
||||||
|
@@ -2507,7 +2507,7 @@ void QuicTransportBase::scheduleAckTimeout() {
|
|||||||
auto timeout = timeMax(
|
auto timeout = timeMax(
|
||||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
wheelTimer.getTickInterval()),
|
wheelTimer.getTickInterval()),
|
||||||
timeMin(kMaxAckTimeout, factoredRtt));
|
timeMin(conn_->ackStates.maxAckDelay, factoredRtt));
|
||||||
auto timeoutMs = folly::chrono::ceil<std::chrono::milliseconds>(timeout);
|
auto timeoutMs = folly::chrono::ceil<std::chrono::milliseconds>(timeout);
|
||||||
VLOG(10) << __func__ << " timeout=" << timeoutMs.count() << "ms"
|
VLOG(10) << __func__ << " timeout=" << timeoutMs.count() << "ms"
|
||||||
<< " factoredRtt=" << factoredRtt.count() << "us"
|
<< " factoredRtt=" << factoredRtt.count() << "us"
|
||||||
|
@@ -2942,6 +2942,20 @@ TEST_F(QuicTransportTest, ScheduleAckTimeout) {
|
|||||||
EXPECT_NEAR(transport_->getAckTimeout()->getTimeRemaining().count(), 25, 5);
|
EXPECT_NEAR(transport_->getAckTimeout()->getTimeRemaining().count(), 25, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(QuicTransportTest, ScheduleAckTimeoutFromMaxAckDelay) {
|
||||||
|
// Make srtt large so we will use maxAckDelay
|
||||||
|
transport_->getConnectionState().lossState.srtt = 25000000us;
|
||||||
|
transport_->getConnectionState().ackStates.maxAckDelay = 10ms;
|
||||||
|
EXPECT_FALSE(transport_->getAckTimeout()->isScheduled());
|
||||||
|
transport_->getConnectionState().pendingEvents.scheduleAckTimeout = true;
|
||||||
|
transport_->onNetworkData(
|
||||||
|
SocketAddress("::1", 10003),
|
||||||
|
NetworkData(
|
||||||
|
IOBuf::copyBuffer("Never on time, always timeout"), Clock::now()));
|
||||||
|
EXPECT_TRUE(transport_->getAckTimeout()->isScheduled());
|
||||||
|
EXPECT_NEAR(transport_->getAckTimeout()->getTimeRemaining().count(), 10, 5);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(QuicTransportTest, CloseTransportCancelsAckTimeout) {
|
TEST_F(QuicTransportTest, CloseTransportCancelsAckTimeout) {
|
||||||
transport_->getConnectionState().lossState.srtt = 25000000us;
|
transport_->getConnectionState().lossState.srtt = 25000000us;
|
||||||
EXPECT_FALSE(transport_->getAckTimeout()->isScheduled());
|
EXPECT_FALSE(transport_->getAckTimeout()->isScheduled());
|
||||||
|
@@ -881,6 +881,7 @@ void QuicClientTransport::startCryptoHandshake() {
|
|||||||
setD6DBasePMTUTransportParameter();
|
setD6DBasePMTUTransportParameter();
|
||||||
setD6DRaiseTimeoutTransportParameter();
|
setD6DRaiseTimeoutTransportParameter();
|
||||||
setD6DProbeTimeoutTransportParameter();
|
setD6DProbeTimeoutTransportParameter();
|
||||||
|
setSupportedExtensionTransportParameters();
|
||||||
|
|
||||||
auto paramsExtension = std::make_shared<ClientTransportParametersExtension>(
|
auto paramsExtension = std::make_shared<ClientTransportParametersExtension>(
|
||||||
conn_->originalVersion.value(),
|
conn_->originalVersion.value(),
|
||||||
@@ -1501,6 +1502,7 @@ bool QuicClientTransport::setCustomTransportParameter(
|
|||||||
// described by the spec.
|
// described by the spec.
|
||||||
if (static_cast<uint16_t>(customParam->getParameterId()) <
|
if (static_cast<uint16_t>(customParam->getParameterId()) <
|
||||||
kCustomTransportParameterThreshold) {
|
kCustomTransportParameterThreshold) {
|
||||||
|
LOG(ERROR) << "invalid parameter id";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1515,6 +1517,7 @@ bool QuicClientTransport::setCustomTransportParameter(
|
|||||||
|
|
||||||
// if a match has been found, we return failure
|
// if a match has been found, we return failure
|
||||||
if (it != customTransportParameters_.end()) {
|
if (it != customTransportParameters_.end()) {
|
||||||
|
LOG(ERROR) << "transport parameter already present";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1605,6 +1608,15 @@ void QuicClientTransport::setD6DProbeTimeoutTransportParameter() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QuicClientTransport::setSupportedExtensionTransportParameters() {
|
||||||
|
if (conn_->transportSettings.minAckDelay.hasValue()) {
|
||||||
|
auto minAckDelayParam = std::make_unique<CustomIntegralTransportParameter>(
|
||||||
|
static_cast<uint64_t>(TransportParameterId::min_ack_delay),
|
||||||
|
conn_->transportSettings.minAckDelay.value().count());
|
||||||
|
customTransportParameters_.push_back(minAckDelayParam->encode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void QuicClientTransport::adjustGROBuffers() {
|
void QuicClientTransport::adjustGROBuffers() {
|
||||||
if (socket_ && conn_) {
|
if (socket_ && conn_) {
|
||||||
if (conn_->transportSettings.numGROBuffers_ > kDefaultNumGROBuffers) {
|
if (conn_->transportSettings.numGROBuffers_ > kDefaultNumGROBuffers) {
|
||||||
|
@@ -208,6 +208,7 @@ class QuicClientTransport
|
|||||||
void setD6DBasePMTUTransportParameter();
|
void setD6DBasePMTUTransportParameter();
|
||||||
void setD6DRaiseTimeoutTransportParameter();
|
void setD6DRaiseTimeoutTransportParameter();
|
||||||
void setD6DProbeTimeoutTransportParameter();
|
void setD6DProbeTimeoutTransportParameter();
|
||||||
|
void setSupportedExtensionTransportParameters();
|
||||||
void adjustGROBuffers();
|
void adjustGROBuffers();
|
||||||
void trackDatagramReceived(size_t len);
|
void trackDatagramReceived(size_t len);
|
||||||
|
|
||||||
|
@@ -99,6 +99,44 @@ KnobFrame decodeKnobFrame(folly::io::Cursor& cursor) {
|
|||||||
return KnobFrame(knobSpace->first, knobId->first, std::move(knobBlob));
|
return KnobFrame(knobSpace->first, knobId->first, std::move(knobBlob));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AckFrequencyFrame decodeAckFrequencyFrame(folly::io::Cursor& cursor) {
|
||||||
|
auto sequenceNumber = decodeQuicInteger(cursor);
|
||||||
|
if (!sequenceNumber) {
|
||||||
|
throw QuicTransportException(
|
||||||
|
"Bad sequence number",
|
||||||
|
quic::TransportErrorCode::FRAME_ENCODING_ERROR,
|
||||||
|
quic::FrameType::ACK_FREQUENCY);
|
||||||
|
}
|
||||||
|
auto packetTolerance = decodeQuicInteger(cursor);
|
||||||
|
if (!packetTolerance) {
|
||||||
|
throw QuicTransportException(
|
||||||
|
"Bad packet tolerance",
|
||||||
|
quic::TransportErrorCode::FRAME_ENCODING_ERROR,
|
||||||
|
quic::FrameType::ACK_FREQUENCY);
|
||||||
|
}
|
||||||
|
auto updateMaxAckDelay = decodeQuicInteger(cursor);
|
||||||
|
if (!updateMaxAckDelay) {
|
||||||
|
throw QuicTransportException(
|
||||||
|
"Bad update max ack delay",
|
||||||
|
quic::TransportErrorCode::FRAME_ENCODING_ERROR,
|
||||||
|
quic::FrameType::ACK_FREQUENCY);
|
||||||
|
}
|
||||||
|
if (cursor.isAtEnd()) {
|
||||||
|
throw QuicTransportException(
|
||||||
|
"Bad ignore order",
|
||||||
|
quic::TransportErrorCode::FRAME_ENCODING_ERROR,
|
||||||
|
quic::FrameType::ACK_FREQUENCY);
|
||||||
|
}
|
||||||
|
auto ignoreOrder = cursor.readBE<uint8_t>();
|
||||||
|
|
||||||
|
AckFrequencyFrame frame;
|
||||||
|
frame.sequenceNumber = sequenceNumber->first;
|
||||||
|
frame.packetTolerance = packetTolerance->first;
|
||||||
|
frame.updateMaxAckDelay = updateMaxAckDelay->first;
|
||||||
|
frame.ignoreOrder = ignoreOrder;
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
ReadAckFrame decodeAckFrame(
|
ReadAckFrame decodeAckFrame(
|
||||||
folly::io::Cursor& cursor,
|
folly::io::Cursor& cursor,
|
||||||
const PacketHeader& header,
|
const PacketHeader& header,
|
||||||
@@ -813,6 +851,8 @@ QuicFrame parseFrame(
|
|||||||
return QuicFrame(decodeHandshakeDoneFrame(cursor));
|
return QuicFrame(decodeHandshakeDoneFrame(cursor));
|
||||||
case FrameType::KNOB:
|
case FrameType::KNOB:
|
||||||
return QuicFrame(decodeKnobFrame(cursor));
|
return QuicFrame(decodeKnobFrame(cursor));
|
||||||
|
case FrameType::ACK_FREQUENCY:
|
||||||
|
return QuicFrame(decodeAckFrequencyFrame(cursor));
|
||||||
}
|
}
|
||||||
} catch (const std::exception&) {
|
} catch (const std::exception&) {
|
||||||
error = true;
|
error = true;
|
||||||
|
@@ -96,6 +96,8 @@ PingFrame decodePingFrame(folly::io::Cursor& cursor);
|
|||||||
|
|
||||||
KnobFrame decodeKnobFrame(folly::io::Cursor& cursor);
|
KnobFrame decodeKnobFrame(folly::io::Cursor& cursor);
|
||||||
|
|
||||||
|
AckFrequencyFrame decodeAckFrequencyFrame(folly::io::Cursor& cursor);
|
||||||
|
|
||||||
DataBlockedFrame decodeDataBlockedFrame(folly::io::Cursor& cursor);
|
DataBlockedFrame decodeDataBlockedFrame(folly::io::Cursor& cursor);
|
||||||
|
|
||||||
StreamDataBlockedFrame decodeStreamDataBlockedFrame(folly::io::Cursor& cursor);
|
StreamDataBlockedFrame decodeStreamDataBlockedFrame(folly::io::Cursor& cursor);
|
||||||
|
@@ -510,6 +510,27 @@ size_t writeSimpleFrame(
|
|||||||
// no space left in packet
|
// no space left in packet
|
||||||
return size_t(0);
|
return size_t(0);
|
||||||
}
|
}
|
||||||
|
case QuicSimpleFrame::Type::AckFrequencyFrame: {
|
||||||
|
const auto ackFrequencyFrame = frame.asAckFrequencyFrame();
|
||||||
|
QuicInteger intFrameType(static_cast<uint64_t>(FrameType::ACK_FREQUENCY));
|
||||||
|
QuicInteger intSequenceNumber(ackFrequencyFrame->sequenceNumber);
|
||||||
|
QuicInteger intPacketTolerance(ackFrequencyFrame->packetTolerance);
|
||||||
|
QuicInteger intUpdateMaxAckDelay(ackFrequencyFrame->updateMaxAckDelay);
|
||||||
|
size_t ackFrequencyFrameLen = intFrameType.getSize() +
|
||||||
|
intSequenceNumber.getSize() + intPacketTolerance.getSize() +
|
||||||
|
intUpdateMaxAckDelay.getSize() + 1 /* ignoreOrder */;
|
||||||
|
if (packetSpaceCheck(spaceLeft, ackFrequencyFrameLen)) {
|
||||||
|
builder.write(intFrameType);
|
||||||
|
builder.write(intSequenceNumber);
|
||||||
|
builder.write(intPacketTolerance);
|
||||||
|
builder.write(intUpdateMaxAckDelay);
|
||||||
|
builder.writeBE(ackFrequencyFrame->ignoreOrder);
|
||||||
|
builder.appendFrame(QuicSimpleFrame(*ackFrequencyFrame));
|
||||||
|
return ackFrequencyFrameLen;
|
||||||
|
}
|
||||||
|
// no space left in packet
|
||||||
|
return size_t(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
folly::assume_unreachable();
|
folly::assume_unreachable();
|
||||||
}
|
}
|
||||||
|
@@ -423,6 +423,8 @@ std::string toString(FrameType frame) {
|
|||||||
return "HANDSHAKE_DONE";
|
return "HANDSHAKE_DONE";
|
||||||
case FrameType::KNOB:
|
case FrameType::KNOB:
|
||||||
return "KNOB";
|
return "KNOB";
|
||||||
|
case FrameType::ACK_FREQUENCY:
|
||||||
|
return "ACK_FREQUENCY";
|
||||||
}
|
}
|
||||||
LOG(WARNING) << "toString has unhandled frame type";
|
LOG(WARNING) << "toString has unhandled frame type";
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
|
@@ -109,6 +109,20 @@ struct KnobFrame {
|
|||||||
Buf blob;
|
Buf blob;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AckFrequencyFrame {
|
||||||
|
uint64_t sequenceNumber; // Used to identify newest.
|
||||||
|
uint64_t packetTolerance; // How many packets before ACKing.
|
||||||
|
uint64_t updateMaxAckDelay; // New max_ack_delay to use.
|
||||||
|
uint8_t ignoreOrder; // Whether to ignore reordering ACKs.
|
||||||
|
|
||||||
|
bool operator==(const AckFrequencyFrame& other) const {
|
||||||
|
return other.sequenceNumber == sequenceNumber &&
|
||||||
|
other.packetTolerance == packetTolerance &&
|
||||||
|
other.updateMaxAckDelay == updateMaxAckDelay &&
|
||||||
|
other.ignoreOrder == ignoreOrder;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AckBlock represents a series of continuous packet sequences from
|
* AckBlock represents a series of continuous packet sequences from
|
||||||
* [startPacket, endPacket]
|
* [startPacket, endPacket]
|
||||||
@@ -644,7 +658,8 @@ struct RetryToken {
|
|||||||
F(MaxStreamsFrame, __VA_ARGS__) \
|
F(MaxStreamsFrame, __VA_ARGS__) \
|
||||||
F(RetireConnectionIdFrame, __VA_ARGS__) \
|
F(RetireConnectionIdFrame, __VA_ARGS__) \
|
||||||
F(HandshakeDoneFrame, __VA_ARGS__) \
|
F(HandshakeDoneFrame, __VA_ARGS__) \
|
||||||
F(KnobFrame, __VA_ARGS__)
|
F(KnobFrame, __VA_ARGS__) \
|
||||||
|
F(AckFrequencyFrame, __VA_ARGS__)
|
||||||
|
|
||||||
DECLARE_VARIANT_TYPE(QuicSimpleFrame, QUIC_SIMPLE_FRAME)
|
DECLARE_VARIANT_TYPE(QuicSimpleFrame, QUIC_SIMPLE_FRAME)
|
||||||
|
|
||||||
|
@@ -32,6 +32,7 @@ enum class TransportParameterId : uint64_t {
|
|||||||
active_connection_id_limit = 0x000e,
|
active_connection_id_limit = 0x000e,
|
||||||
initial_source_connection_id = 0x000f,
|
initial_source_connection_id = 0x000f,
|
||||||
retry_source_connection_id = 0x0010,
|
retry_source_connection_id = 0x0010,
|
||||||
|
min_ack_delay = 0xff02de1a,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TransportParameter {
|
struct TransportParameter {
|
||||||
|
@@ -71,6 +71,15 @@ void addQuicSimpleFrameToEvent(
|
|||||||
frame.knobSpace, frame.id, frame.blob->length()));
|
frame.knobSpace, frame.id, frame.blob->length()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case quic::QuicSimpleFrame::Type::AckFrequencyFrame: {
|
||||||
|
const quic::AckFrequencyFrame& frame = *simpleFrame.asAckFrequencyFrame();
|
||||||
|
event->frames.push_back(std::make_unique<quic::AckFrequencyFrameLog>(
|
||||||
|
frame.sequenceNumber,
|
||||||
|
frame.packetTolerance,
|
||||||
|
frame.updateMaxAckDelay,
|
||||||
|
frame.ignoreOrder));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@@ -79,6 +79,8 @@ folly::StringPiece toQlogString(FrameType frame) {
|
|||||||
return "handshake_done";
|
return "handshake_done";
|
||||||
case FrameType::KNOB:
|
case FrameType::KNOB:
|
||||||
return "knob";
|
return "knob";
|
||||||
|
case FrameType::ACK_FREQUENCY:
|
||||||
|
return "ack_frequency";
|
||||||
}
|
}
|
||||||
folly::assume_unreachable();
|
folly::assume_unreachable();
|
||||||
}
|
}
|
||||||
|
@@ -110,6 +110,16 @@ folly::dynamic KnobFrameLog::toDynamic() const {
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
folly::dynamic AckFrequencyFrameLog::toDynamic() const {
|
||||||
|
folly::dynamic d = folly::dynamic::object();
|
||||||
|
d["frame_type"] = toQlogString(FrameType::ACK_FREQUENCY);
|
||||||
|
d["sequence_number"] = sequenceNumber;
|
||||||
|
d["packet_tolerance"] = packetTolerance;
|
||||||
|
d["update_max_ack_delay"] = updateMaxAckDelay;
|
||||||
|
d["ignore_order"] = ignoreOrder;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
folly::dynamic StreamDataBlockedFrameLog::toDynamic() const {
|
folly::dynamic StreamDataBlockedFrameLog::toDynamic() const {
|
||||||
folly::dynamic d = folly::dynamic::object();
|
folly::dynamic d = folly::dynamic::object();
|
||||||
d["frame_type"] = toQlogString(FrameType::STREAM_DATA_BLOCKED);
|
d["frame_type"] = toQlogString(FrameType::STREAM_DATA_BLOCKED);
|
||||||
|
@@ -145,6 +145,26 @@ class KnobFrameLog : public QLogFrame {
|
|||||||
FOLLY_NODISCARD folly::dynamic toDynamic() const override;
|
FOLLY_NODISCARD folly::dynamic toDynamic() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AckFrequencyFrameLog : public QLogFrame {
|
||||||
|
public:
|
||||||
|
uint64_t sequenceNumber;
|
||||||
|
uint64_t packetTolerance;
|
||||||
|
uint64_t updateMaxAckDelay;
|
||||||
|
bool ignoreOrder;
|
||||||
|
|
||||||
|
explicit AckFrequencyFrameLog(
|
||||||
|
uint64_t sequenceNumberIn,
|
||||||
|
uint64_t packetToleranceIn,
|
||||||
|
uint64_t updateMaxAckDelayIn,
|
||||||
|
bool ignoreOrderIn)
|
||||||
|
: sequenceNumber(sequenceNumberIn),
|
||||||
|
packetTolerance(packetToleranceIn),
|
||||||
|
updateMaxAckDelay(updateMaxAckDelayIn),
|
||||||
|
ignoreOrder(ignoreOrderIn) {}
|
||||||
|
~AckFrequencyFrameLog() override = default;
|
||||||
|
FOLLY_NODISCARD folly::dynamic toDynamic() const override;
|
||||||
|
};
|
||||||
|
|
||||||
class StreamDataBlockedFrameLog : public QLogFrame {
|
class StreamDataBlockedFrameLog : public QLogFrame {
|
||||||
public:
|
public:
|
||||||
StreamId streamId;
|
StreamId streamId;
|
||||||
|
@@ -136,6 +136,8 @@ void processClientInitialParams(
|
|||||||
auto d6dProbeTimeout = getIntegerParameter(
|
auto d6dProbeTimeout = getIntegerParameter(
|
||||||
static_cast<TransportParameterId>(kD6DProbeTimeoutParameterId),
|
static_cast<TransportParameterId>(kD6DProbeTimeoutParameterId),
|
||||||
clientParams.parameters);
|
clientParams.parameters);
|
||||||
|
auto minAckDelay = getIntegerParameter(
|
||||||
|
TransportParameterId::min_ack_delay, clientParams.parameters);
|
||||||
if (conn.version == QuicVersion::QUIC_DRAFT) {
|
if (conn.version == QuicVersion::QUIC_DRAFT) {
|
||||||
auto initialSourceConnId = getConnIdParameter(
|
auto initialSourceConnId = getConnIdParameter(
|
||||||
TransportParameterId::initial_source_connection_id,
|
TransportParameterId::initial_source_connection_id,
|
||||||
@@ -186,6 +188,9 @@ void processClientInitialParams(
|
|||||||
}
|
}
|
||||||
conn.peerAckDelayExponent =
|
conn.peerAckDelayExponent =
|
||||||
ackDelayExponent.value_or(kDefaultAckDelayExponent);
|
ackDelayExponent.value_or(kDefaultAckDelayExponent);
|
||||||
|
if (minAckDelay.hasValue()) {
|
||||||
|
conn.peerMinAckDelay = std::chrono::microseconds(minAckDelay.value());
|
||||||
|
}
|
||||||
|
|
||||||
// Default to max because we can probe PMTU now, and this will be the upper
|
// Default to max because we can probe PMTU now, and this will be the upper
|
||||||
// limit
|
// limit
|
||||||
|
@@ -33,6 +33,9 @@ struct AckState {
|
|||||||
// Next PacketNum we will send for packet in this packet number space
|
// Next PacketNum we will send for packet in this packet number space
|
||||||
PacketNum nextPacketNum{0};
|
PacketNum nextPacketNum{0};
|
||||||
AckBlocks acks;
|
AckBlocks acks;
|
||||||
|
bool ignoreReorder{false};
|
||||||
|
folly::Optional<uint64_t> tolerance;
|
||||||
|
folly::Optional<uint64_t> ackFrequencySequenceNumber;
|
||||||
// Flag indicating that if we need to send ack immediately. This will be set
|
// Flag indicating that if we need to send ack immediately. This will be set
|
||||||
// to true if we got packets with retransmittable data and haven't sent the
|
// to true if we got packets with retransmittable data and haven't sent the
|
||||||
// ack for the first time.
|
// ack for the first time.
|
||||||
@@ -57,6 +60,7 @@ struct AckStates {
|
|||||||
AckState handshakeAckState;
|
AckState handshakeAckState;
|
||||||
// AckState for acks to peer packets in AppData packet number space.
|
// AckState for acks to peer packets in AppData packet number space.
|
||||||
AckState appDataAckState;
|
AckState appDataAckState;
|
||||||
|
std::chrono::microseconds maxAckDelay{kMaxAckTimeout};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace quic
|
} // namespace quic
|
||||||
|
@@ -90,10 +90,17 @@ void updateAckSendStateOnRecvPacket(
|
|||||||
DCHECK(!pktHasCryptoData || pktHasRetransmittableData);
|
DCHECK(!pktHasCryptoData || pktHasRetransmittableData);
|
||||||
auto thresh = kNonRtxRxPacketsPendingBeforeAck;
|
auto thresh = kNonRtxRxPacketsPendingBeforeAck;
|
||||||
if (pktHasRetransmittableData || ackState.numRxPacketsRecvd) {
|
if (pktHasRetransmittableData || ackState.numRxPacketsRecvd) {
|
||||||
thresh = ackState.largestReceivedPacketNum.value_or(0) >
|
if (ackState.tolerance.hasValue()) {
|
||||||
conn.transportSettings.rxPacketsBeforeAckInitThreshold
|
thresh = ackState.tolerance.value();
|
||||||
? conn.transportSettings.rxPacketsBeforeAckAfterInit
|
} else {
|
||||||
: conn.transportSettings.rxPacketsBeforeAckBeforeInit;
|
thresh = ackState.largestReceivedPacketNum.value_or(0) >
|
||||||
|
conn.transportSettings.rxPacketsBeforeAckInitThreshold
|
||||||
|
? conn.transportSettings.rxPacketsBeforeAckAfterInit
|
||||||
|
: conn.transportSettings.rxPacketsBeforeAckBeforeInit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ackState.ignoreReorder) {
|
||||||
|
pktOutOfOrder = false;
|
||||||
}
|
}
|
||||||
if (pktHasRetransmittableData) {
|
if (pktHasRetransmittableData) {
|
||||||
if (pktHasCryptoData || pktOutOfOrder ||
|
if (pktHasCryptoData || pktOutOfOrder ||
|
||||||
|
@@ -55,6 +55,7 @@ folly::Optional<QuicSimpleFrame> updateSimpleFrameOnPacketClone(
|
|||||||
case QuicSimpleFrame::Type::MaxStreamsFrame:
|
case QuicSimpleFrame::Type::MaxStreamsFrame:
|
||||||
case QuicSimpleFrame::Type::HandshakeDoneFrame:
|
case QuicSimpleFrame::Type::HandshakeDoneFrame:
|
||||||
case QuicSimpleFrame::Type::KnobFrame:
|
case QuicSimpleFrame::Type::KnobFrame:
|
||||||
|
case QuicSimpleFrame::Type::AckFrequencyFrame:
|
||||||
case QuicSimpleFrame::Type::RetireConnectionIdFrame:
|
case QuicSimpleFrame::Type::RetireConnectionIdFrame:
|
||||||
// TODO junqiw
|
// TODO junqiw
|
||||||
return QuicSimpleFrame(frame);
|
return QuicSimpleFrame(frame);
|
||||||
@@ -133,6 +134,7 @@ void updateSimpleFrameOnPacketLoss(
|
|||||||
case QuicSimpleFrame::Type::MaxStreamsFrame:
|
case QuicSimpleFrame::Type::MaxStreamsFrame:
|
||||||
case QuicSimpleFrame::Type::RetireConnectionIdFrame:
|
case QuicSimpleFrame::Type::RetireConnectionIdFrame:
|
||||||
case QuicSimpleFrame::Type::KnobFrame:
|
case QuicSimpleFrame::Type::KnobFrame:
|
||||||
|
case QuicSimpleFrame::Type::AckFrequencyFrame:
|
||||||
conn.pendingEvents.frames.push_back(frame);
|
conn.pendingEvents.frames.push_back(frame);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -294,6 +296,24 @@ bool updateSimpleFrameOnPacketReceived(
|
|||||||
knobFrame.knobSpace, knobFrame.id, knobFrame.blob->clone());
|
knobFrame.knobSpace, knobFrame.id, knobFrame.blob->clone());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case QuicSimpleFrame::Type::AckFrequencyFrame: {
|
||||||
|
if (!conn.transportSettings.minAckDelay.hasValue()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const auto ackFrequencyFrame = frame.asAckFrequencyFrame();
|
||||||
|
auto& ackState = conn.ackStates.appDataAckState;
|
||||||
|
if (!ackState.ackFrequencySequenceNumber ||
|
||||||
|
ackFrequencyFrame->sequenceNumber >
|
||||||
|
ackState.ackFrequencySequenceNumber.value()) {
|
||||||
|
ackState.tolerance = ackFrequencyFrame->packetTolerance;
|
||||||
|
ackState.ignoreReorder = ackFrequencyFrame->ignoreOrder;
|
||||||
|
conn.ackStates.maxAckDelay =
|
||||||
|
std::chrono::microseconds(std::max<uint64_t>(
|
||||||
|
conn.transportSettings.minAckDelay->count(),
|
||||||
|
ackFrequencyFrame->updateMaxAckDelay));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
folly::assume_unreachable();
|
folly::assume_unreachable();
|
||||||
}
|
}
|
||||||
|
@@ -645,6 +645,9 @@ struct QuicConnectionStateBase : public folly::DelayedDestruction {
|
|||||||
// Value of the negotiated ack delay exponent.
|
// Value of the negotiated ack delay exponent.
|
||||||
uint64_t peerAckDelayExponent{kDefaultAckDelayExponent};
|
uint64_t peerAckDelayExponent{kDefaultAckDelayExponent};
|
||||||
|
|
||||||
|
// The value of the peer's min_ack_delay, for creating ACK_FREQUENCY frames.
|
||||||
|
folly::Optional<std::chrono::microseconds> peerMinAckDelay;
|
||||||
|
|
||||||
// Idle timeout advertised by the peer. Initially sets it to the maximum value
|
// Idle timeout advertised by the peer. Initially sets it to the maximum value
|
||||||
// until the handshake sets the timeout.
|
// until the handshake sets the timeout.
|
||||||
std::chrono::milliseconds peerIdleTimeout{kMaxIdleTimeout};
|
std::chrono::milliseconds peerIdleTimeout{kMaxIdleTimeout};
|
||||||
|
@@ -198,6 +198,7 @@ struct TransportSettings {
|
|||||||
kDefaultRxPacketsBeforeAckInitThreshold};
|
kDefaultRxPacketsBeforeAckInitThreshold};
|
||||||
uint16_t rxPacketsBeforeAckBeforeInit{kDefaultRxPacketsBeforeAckBeforeInit};
|
uint16_t rxPacketsBeforeAckBeforeInit{kDefaultRxPacketsBeforeAckBeforeInit};
|
||||||
uint16_t rxPacketsBeforeAckAfterInit{kDefaultRxPacketsBeforeAckAfterInit};
|
uint16_t rxPacketsBeforeAckAfterInit{kDefaultRxPacketsBeforeAckAfterInit};
|
||||||
|
folly::Optional<std::chrono::microseconds> minAckDelay;
|
||||||
// Limits the amount of data that should be buffered in a QuicSocket.
|
// Limits the amount of data that should be buffered in a QuicSocket.
|
||||||
// If the amount of data in the buffer equals or exceeds this amount, then
|
// If the amount of data in the buffer equals or exceeds this amount, then
|
||||||
// the callback registered through notifyPendingWriteOnConnection() will
|
// the callback registered through notifyPendingWriteOnConnection() will
|
||||||
|
@@ -220,6 +220,38 @@ TEST_P(UpdateAckStateTest, TestUpdateAckStateFrequency) {
|
|||||||
EXPECT_FALSE(conn.pendingEvents.scheduleAckTimeout);
|
EXPECT_FALSE(conn.pendingEvents.scheduleAckTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_P(UpdateAckStateTest, TestUpdateAckStateFrequencyFromTolerance) {
|
||||||
|
QuicServerConnectionState conn(
|
||||||
|
FizzServerQuicHandshakeContext::Builder().build());
|
||||||
|
PacketNum nextPacketNum = 1;
|
||||||
|
auto& ackState = getAckState(conn, GetParam());
|
||||||
|
ackState.largestReceivedPacketNum = nextPacketNum - 1;
|
||||||
|
ackState.tolerance = 2;
|
||||||
|
for (nextPacketNum; nextPacketNum <= 10; nextPacketNum++) {
|
||||||
|
updateAckState(conn, GetParam(), nextPacketNum, true, false, Clock::now());
|
||||||
|
if (nextPacketNum % 2 == 0) {
|
||||||
|
EXPECT_TRUE(ackState.needsToSendAckImmediately);
|
||||||
|
EXPECT_FALSE(conn.pendingEvents.scheduleAckTimeout);
|
||||||
|
} else {
|
||||||
|
EXPECT_FALSE(ackState.needsToSendAckImmediately);
|
||||||
|
EXPECT_TRUE(conn.pendingEvents.scheduleAckTimeout);
|
||||||
|
}
|
||||||
|
EXPECT_EQ(ackState.numRxPacketsRecvd, nextPacketNum % 2);
|
||||||
|
}
|
||||||
|
ackState.tolerance = 10;
|
||||||
|
for (nextPacketNum; nextPacketNum <= 40; nextPacketNum++) {
|
||||||
|
updateAckState(conn, GetParam(), nextPacketNum, true, false, Clock::now());
|
||||||
|
if (nextPacketNum % 10 == 0) {
|
||||||
|
EXPECT_TRUE(ackState.needsToSendAckImmediately);
|
||||||
|
EXPECT_FALSE(conn.pendingEvents.scheduleAckTimeout);
|
||||||
|
} else {
|
||||||
|
EXPECT_FALSE(ackState.needsToSendAckImmediately);
|
||||||
|
EXPECT_TRUE(conn.pendingEvents.scheduleAckTimeout);
|
||||||
|
}
|
||||||
|
EXPECT_EQ(ackState.numRxPacketsRecvd, nextPacketNum % 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(UpdateAckStateTest, UpdateAckStateOnAckTimeout) {
|
TEST_F(UpdateAckStateTest, UpdateAckStateOnAckTimeout) {
|
||||||
QuicConnectionStateBase conn(QuicNodeType::Client);
|
QuicConnectionStateBase conn(QuicNodeType::Client);
|
||||||
auto& initialAckState = getAckState(conn, PacketNumberSpace::Initial);
|
auto& initialAckState = getAckState(conn, PacketNumberSpace::Initial);
|
||||||
@@ -352,6 +384,16 @@ TEST_P(UpdateAckStateTest, UpdateAckSendStateOnRecvPacketsRxOutOfOrder) {
|
|||||||
EXPECT_FALSE(verifyToScheduleAckTimeout(conn));
|
EXPECT_FALSE(verifyToScheduleAckTimeout(conn));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_P(UpdateAckStateTest, UpdateAckSendStateOnRecvPacketsRxOutOfOrderIgnore) {
|
||||||
|
// Retransmittable & out of order: don't ack immediately if ignoring order.
|
||||||
|
QuicConnectionStateBase conn(QuicNodeType::Client);
|
||||||
|
auto& ackState = getAckState(conn, GetParam());
|
||||||
|
ackState.ignoreReorder = true;
|
||||||
|
updateAckSendStateOnRecvPacket(conn, ackState, true, true, false);
|
||||||
|
EXPECT_FALSE(verifyToAckImmediately(conn, ackState));
|
||||||
|
EXPECT_TRUE(verifyToScheduleAckTimeout(conn));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_P(UpdateAckStateTest, UpdateAckSendStateOnRecvPacketsNonRxOutOfOrder) {
|
TEST_P(UpdateAckStateTest, UpdateAckSendStateOnRecvPacketsNonRxOutOfOrder) {
|
||||||
// Non-retransmittable & out of order: not ack immediately
|
// Non-retransmittable & out of order: not ack immediately
|
||||||
QuicConnectionStateBase conn(QuicNodeType::Client);
|
QuicConnectionStateBase conn(QuicNodeType::Client);
|
||||||
|
Reference in New Issue
Block a user