quic/state/AckEvent.h (145 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <folly/Optional.h>
#include <folly/container/F14Map.h>
#include <quic/codec/Types.h>
#include <quic/congestion_control/CongestionController.h>
#include <quic/state/OutstandingPacket.h>
namespace quic {
struct AckEvent {
struct AckPacket;
/**
* Returns the AckPacket associated with the AckEvent's RttSample.
*
* Can be used to get packet metadata, including send time, app limited state,
* and other aspects. For RTT measurements, this can be used to determine the
* number of packets / bytes inflight at the time the corresponding packet was
* sent, which in turn can be used to infer whether the RTT measurement could
* have been subject to self-induced congestion.
*
* If the OutstandingPacket with the largestAckedPacket packet number had
* already been acked or removed from the list of list of OutstandingPackets,
* either due to being marked lost or acked by an earlier AckEvent, then this
* information will be unavailable.
*
* Equivalent to getLargestAckedPacket() unless this is an implicit AckEvent
* (for which RttSamples are unavailable); this helper exists to make it
* easier to find this information.
*/
[[nodiscard]] const AckPacket* FOLLY_NULLABLE
getRttSampleAckedPacket() const {
if (!rttSample) {
return nullptr;
}
return getLargestAckedPacket();
}
/**
* Returns the AckPacket associated with the largestAckedPacket.
*
* The largestAckedPacket is included in the AckFrame received from sender.
*
* Can be used to get packet metadata, including send time, app limited state,
* and other aspects.
*
* If the OutstandingPacket with the largestAckedPacket packet number had
* already been acked or removed from the list of list of OutstandingPackets,
* either due to being marked lost or acked by an earlier AckEvent, then this
* information will be unavailable.
*/
[[nodiscard]] const AckPacket* FOLLY_NULLABLE getLargestAckedPacket() const {
for (const auto& packet : ackedPackets) {
if (packet.packetNum == largestAckedPacket) {
return &packet;
}
}
return nullptr;
}
/**
* Returns the AckPacket associated with the largestNewlyAckedPacket.
*
* Can be used to get packet metadata, including send time, app limited state,
* and other aspects.
*/
[[nodiscard]] const AckPacket* FOLLY_NULLABLE
getLargestNewlyAckedPacket() const {
if (!largestNewlyAckedPacket.has_value()) {
return nullptr;
}
for (const auto& packet : ackedPackets) {
if (packet.packetNum == largestNewlyAckedPacket) {
return &packet;
}
}
return nullptr;
}
// ack receive time
const TimePoint ackTime;
// ack receive time minus ack delay.
const TimePoint adjustedAckTime;
// ack delay
//
// the ack delay is the amount of time between the remote receiving
// largestAckedPacket and the remote generating the AckFrame associated with
// this AckEvent.
//
// different AckFrame can have the same largestAckedPacket with different ack
// blocks (ranges) in the case of reordering; under such circumstances, you
// cannot use the ack delay if the largestAckedPacket was already acknowledged
// by a previous AckFrame.
const std::chrono::microseconds ackDelay;
// packet number space that acked packets are in.
const PacketNumberSpace packetNumberSpace;
// the largest acked packet included in the AckFrame received from sender.
//
// this may not be the same as largestNewlyAckedPacket (below) if the
// OutstandingPacket with this packet number had already been removed from the
// list of OutstandingPackets, either due to being marked lost or acked.
const PacketNum largestAckedPacket;
// for all packets (newly) acked during this event, sum of encoded sizes
// encoded size includes header and body
//
// this value does not directly translate to the number of stream bytes newly
// acked; see the DetailsPerStream structure in each of the AckedPackets to
// determine information about stream bytes.
uint64_t ackedBytes{0};
// the highest packet number newly acked during processing of this event.
//
// this may not be the same as the largestAckedPacket if the OutstandingPacket
// with that packet number had already been acked or removed from the list of
// list of OutstandingPackets, either due to being marked lost or acked.
//
// the reason that this is an optional type is that we construct an
// AckEvent first, then go through the acked packets that are still
// outstanding and figure out the largest newly acked packet along the way.
folly::Optional<PacketNum> largestNewlyAckedPacket;
// when largestNewlyAckedPacket was sent
TimePoint largestNewlyAckedPacketSentTime;
// RTT sample with ack delay included.
//
// not available if largestAckedPacket already acked or declared lost
folly::Optional<std::chrono::microseconds> rttSample;
// RTT sample with ack delay removed.
//
// not available if largestAckedPacket already acked or declared lost
folly::Optional<std::chrono::microseconds> rttSampleNoAckDelay;
// Congestion controller state after processing of AckEvent.
//
// Optional to handle cases where congestion controller not used.
folly::Optional<CongestionController::State> ccState;
/**
* Booleans grouped together to avoid padding.
*/
// if this AckEvent came from an implicit ACK rather than a real one
bool implicit{false};
// whether the transport was app limited when largestNewlyAckedPacket was sent
bool largestNewlyAckedPacketAppLimited{false};
/**
* Container to store information about ACKed packets
*/
struct AckPacket {
// Sequence number of previously outstanding (now acked) packet
quic::PacketNum packetNum;
// Metadata of the previously outstanding (now acked) packet
OutstandingPacketMetadata outstandingPacketMetadata;
struct StreamDetails {
uint64_t streamBytesAcked{0};
uint64_t streamBytesAckedByRetrans{0};
folly::Optional<uint64_t> maybeNewDeliveryOffset;
// definition for DupAckedStreamIntervalSet
// we expect this to be rare, any thus only allocate a single position
template <class T>
using DupAckedStreamIntervalSetVec =
SmallVec<T, 1 /* stack size */, uint16_t>;
using DupAckedStreamIntervals =
IntervalSet<uint64_t, 1, DupAckedStreamIntervalSetVec>;
// Intervals that had already been ACKed.
//
// Requires ACK processing for packets spuriously marked lost is enabled
DupAckedStreamIntervals dupAckedStreamIntervals;
};
// Structure with information about each stream with frames in ACKed packet
class DetailsPerStream
: private folly::F14FastMap<StreamId, StreamDetails> {
public:
/**
* Record that a frame contained in ACKed packet was marked as delivered.
*
* Specifically, during processing of this ACK, we were able to fill in a
* hole in the stream IntervalSet. This means that the intervals covered
* by said frame had not been delivered by another packet.
*
* If said frame had previously been sent in some previous packet before
* being sent in the packet that we are processing the ACK for now, then
* we can conclude that a retransmission enabled this frame to be
* delivered.
*
* See recordFrameAlreadyDelivered for the case where a frame contained in
* an ACKed packet had already been marked as delivered.
*
* @param frame The frame that is being processed.
* @param retransmission Whether this frame was being retransmitted in the
* packet being processed. If true, the frame was
* previously sent in some earlier packet.
*/
void recordFrameDelivered(
const WriteStreamFrame& frame,
const bool retransmission);
/**
* Record that a frame had already been marked as delivered.
*
* This can occur if said frame was sent multiple times (e.g., in multiple
* packets) and an ACK for a different packet containing the frame was
* already processed. More specifically,, the hole in the stream
* IntervalSet associated with this frame was marked as delivered when
* some other packet's ACK was processed.
*
* Note that packet(s) carrying the frame may have been acknowledged at
* the same time by the remote (e.g., in the same ACK block / message), in
* which case we cannot discern "which" packet arrived first — we can only
* state that multiple packets(s) carrying the same frame successfully
* reached the remote.
*
* @param frame The frame that is being processed and that was
* marked as delivered by some previous packet.
* @param retransmission Whether this frame was being retransmitted in the
* packet being processed. If true, the frame was
* previously sent in some earlier packet. This is
* generally expected to be true for the "already
* delivered" scenario; the exception would be
* packet reordering.
*/
void recordFrameAlreadyDelivered(
const WriteStreamFrame& frame,
const bool retransmission);
/**
* Record a delivery offset update (increase) for a stream ID.
*/
void recordDeliveryOffsetUpdate(StreamId streamId, uint64_t newOffset);
[[nodiscard]] auto at(StreamId id) const {
return folly::F14FastMap<StreamId, StreamDetails>::at(id);
}
[[nodiscard]] auto begin() const {
return cbegin();
}
[[nodiscard]] auto end() const {
return cend();
}
using folly::F14FastMap<StreamId, StreamDetails>::cbegin;
using folly::F14FastMap<StreamId, StreamDetails>::cend;
using folly::F14FastMap<StreamId, StreamDetails>::const_iterator;
using folly::F14FastMap<StreamId, StreamDetails>::empty;
using folly::F14FastMap<StreamId, StreamDetails>::find;
using folly::F14FastMap<StreamId, StreamDetails>::mapped_type;
using folly::F14FastMap<StreamId, StreamDetails>::size;
using folly::F14FastMap<StreamId, StreamDetails>::value_type;
};
// Details for each active stream that was impacted by an ACKed frame
DetailsPerStream detailsPerStream;
// LastAckedPacketInfo from this acked packet'r original sent
// OutstandingPacket structure.
folly::Optional<OutstandingPacket::LastAckedPacketInfo> lastAckedPacketInfo;
// Whether this packet was sent when CongestionController is in
// app-limited state.
bool isAppLimited;
struct Builder {
Builder&& setPacketNum(quic::PacketNum packetNumIn);
Builder&& setOutstandingPacketMetadata(
OutstandingPacketMetadata&& outstandingPacketMetadataIn);
Builder&& setDetailsPerStream(DetailsPerStream&& detailsPerStreamIn);
Builder&& setLastAckedPacketInfo(
folly::Optional<OutstandingPacket::LastAckedPacketInfo>&&
lastAckedPacketInfoIn);
Builder&& setAppLimited(bool appLimitedIn);
AckPacket build() &&;
explicit Builder() = default;
private:
folly::Optional<quic::PacketNum> packetNum;
folly::Optional<OutstandingPacketMetadata> outstandingPacketMetadata;
folly::Optional<DetailsPerStream> detailsPerStream;
folly::Optional<OutstandingPacket::LastAckedPacketInfo>
lastAckedPacketInfo;
bool isAppLimited{false};
};
private:
explicit AckPacket(
quic::PacketNum packetNumIn,
OutstandingPacketMetadata&& outstandingPacketMetadataIn,
DetailsPerStream&& detailsPerStreamIn,
folly::Optional<OutstandingPacket::LastAckedPacketInfo>
lastAckedPacketInfoIn,
bool isAppLimitedIn);
};
// Information about each packet ACKed during this event
std::vector<AckPacket> ackedPackets;
struct BuilderFields {
folly::Optional<TimePoint> maybeAckTime;
folly::Optional<TimePoint> maybeAdjustedAckTime;
folly::Optional<std::chrono::microseconds> maybeAckDelay;
folly::Optional<PacketNumberSpace> maybePacketNumberSpace;
folly::Optional<PacketNum> maybeLargestAckedPacket;
bool isImplicitAck{false};
explicit BuilderFields() = default;
};
struct Builder : public BuilderFields {
Builder&& setAckTime(TimePoint ackTimeIn);
Builder&& setAdjustedAckTime(TimePoint adjustedAckTimeIn);
Builder&& setAckDelay(std::chrono::microseconds ackDelay);
Builder&& setPacketNumberSpace(PacketNumberSpace packetNumberSpaceIn);
Builder&& setLargestAckedPacket(PacketNum largestAckedPacketIn);
Builder&& setIsImplicitAck(bool isImplicitAckIn);
AckEvent build() &&;
explicit Builder() = default;
};
// Use builder to construct.
explicit AckEvent(BuilderFields&& fields);
};
} // namespace quic