1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-04-18 17:24:03 +03:00
mvfst/quic/api/Observer.h
Hani Damlaj 2660a288b3 Update Company Name
Summary: - as title

Reviewed By: lnicco

Differential Revision: D33513410

fbshipit-source-id: 282b6f512cf83b9abb7990402661135b658f7bd1
2022-01-13 12:07:48 -08:00

551 lines
17 KiB
C++

/*
* 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 <quic/QuicException.h>
#include <quic/common/SmallVec.h>
#include <quic/d6d/Types.h>
#include <quic/state/AckEvent.h>
#include <quic/state/OutstandingPacket.h>
#include <quic/state/QuicStreamUtilities.h>
namespace folly {
class EventBase;
}
namespace quic {
class QuicSocket;
/**
* ===== Observer API =====
*/
/**
* Observer of socket events.
*/
class Observer {
public:
/**
* Observer configuration.
*
* Specifies events observer wants to receive.
*/
struct Config {
virtual ~Config() = default;
// following flags enable support for various callbacks.
// observer and socket lifecycle callbacks are always enabled.
bool evbEvents{false};
bool packetsWrittenEvents{false};
bool appRateLimitedEvents{false};
bool lossEvents{false};
bool spuriousLossEvents{false};
bool pmtuEvents{false};
bool rttSamples{false};
bool knobFrameEvents{false};
bool streamEvents{false};
bool acksProcessedEvents{false};
virtual void enableAllEvents() {
evbEvents = true;
packetsWrittenEvents = true;
appRateLimitedEvents = true;
rttSamples = true;
lossEvents = true;
spuriousLossEvents = true;
pmtuEvents = true;
knobFrameEvents = true;
streamEvents = true;
acksProcessedEvents = true;
}
/**
* Returns a config where all events are enabled.
*/
static Config getConfigAllEventsEnabled() {
Config config = {};
config.enableAllEvents();
return config;
}
};
/**
* Constructor for observer, uses default config (all callbacks disabled).
*/
Observer() : Observer(Config()) {}
/**
* Constructor for observer.
*
* @param config Config, defaults to auxilary instrumentaton disabled.
*/
explicit Observer(const Config& observerConfig)
: observerConfig_(observerConfig) {}
virtual ~Observer() = default;
/**
* Returns observers configuration.
*/
const Config& getConfig() {
return observerConfig_;
}
struct WriteEvent {
[[nodiscard]] const std::deque<OutstandingPacket>& getOutstandingPackets()
const {
return outstandingPackets;
}
// Reference to the current list of outstanding packets.
const std::deque<OutstandingPacket>& outstandingPackets;
// Monotonically increasing number assigned to each write operation.
const uint64_t writeCount;
struct BuilderFields {
folly::Optional<
std::reference_wrapper<const std::deque<OutstandingPacket>>>
maybeOutstandingPacketsRef;
folly::Optional<uint64_t> maybeWriteCount;
explicit BuilderFields() = default;
};
struct Builder : public BuilderFields {
Builder&& setOutstandingPackets(
const std::deque<OutstandingPacket>& outstandingPacketsIn);
Builder&& setWriteCount(const uint64_t writeCountIn);
WriteEvent build() &&;
explicit Builder() = default;
};
// Do not support copy or move given that outstanding packets is a ref.
WriteEvent(const WriteEvent&) = delete;
WriteEvent(WriteEvent&&) = delete;
WriteEvent& operator=(const WriteEvent&) = delete;
WriteEvent& operator=(WriteEvent&& rhs) = delete;
// Use builder to construct.
explicit WriteEvent(const BuilderFields& builderFields);
};
struct AppLimitedEvent : public WriteEvent {
struct Builder : public WriteEvent::BuilderFields {
Builder&& setOutstandingPackets(
const std::deque<OutstandingPacket>& outstandingPacketsIn);
Builder&& setWriteCount(const uint64_t writeCountIn);
AppLimitedEvent build() &&;
explicit Builder() = default;
};
// Use builder to construct.
explicit AppLimitedEvent(BuilderFields&& builderFields);
};
struct PacketsWrittenEvent : public WriteEvent {
// Number of packets just written, including ACK eliciting packets.
const uint64_t numPacketsWritten;
// Number of ACK eliciting packets written.
// These packets will appear in outstandingPackets.
const uint64_t numAckElicitingPacketsWritten;
struct BuilderFields : public WriteEvent::BuilderFields {
folly::Optional<uint64_t> maybeNumPacketsWritten;
folly::Optional<uint64_t> maybeNumAckElicitingPacketsWritten;
explicit BuilderFields() = default;
};
struct Builder : public BuilderFields {
Builder&& setOutstandingPackets(
const std::deque<OutstandingPacket>& outstandingPacketsIn);
Builder&& setWriteCount(const uint64_t writeCountIn);
Builder&& setNumPacketsWritten(const uint64_t numPacketsWrittenIn);
Builder&& setNumAckElicitingPacketsWritten(
const uint64_t numAckElicitingPacketsWrittenIn);
PacketsWrittenEvent build() &&;
explicit Builder() = default;
};
// Use builder to construct.
explicit PacketsWrittenEvent(BuilderFields&& builderFields);
};
struct AcksProcessedEvent {
[[nodiscard]] const std::vector<AckEvent>& getAckEvents() const {
return ackEvents;
}
// Reference to last processed set of ack events.
const std::vector<AckEvent>& ackEvents;
struct BuilderFields {
folly::Optional<std::reference_wrapper<const std::vector<AckEvent>>>
maybeAckEventsRef;
explicit BuilderFields() = default;
};
struct Builder : public BuilderFields {
Builder&& setAckEvents(const std::vector<AckEvent>& ackEventsIn);
AcksProcessedEvent build() &&;
explicit Builder() = default;
};
// Use builder to construct.
explicit AcksProcessedEvent(BuilderFields builderFields);
};
struct LostPacket {
explicit LostPacket(
bool lostbytimeout,
bool lostbyreorder,
const quic::OutstandingPacket& pkt)
: lostByTimeout(lostbytimeout),
lostByReorderThreshold(lostbyreorder),
packet(pkt) {}
bool lostByTimeout{false};
bool lostByReorderThreshold{false};
const quic::OutstandingPacket packet;
};
struct LossEvent {
explicit LossEvent(TimePoint time = Clock::now()) : lossTime(time) {}
bool hasPackets() {
return lostPackets.size() > 0;
}
void addLostPacket(
bool lostByTimeout,
bool lostByReorder,
const quic::OutstandingPacket& packet) {
lostPackets.emplace_back(lostByTimeout, lostByReorder, packet);
}
const TimePoint lossTime;
std::vector<LostPacket> lostPackets;
};
struct PacketRTT {
explicit PacketRTT(
TimePoint rcvTimeIn,
std::chrono::microseconds rttSampleIn,
std::chrono::microseconds ackDelayIn,
const quic::OutstandingPacket& pkt)
: rcvTime(rcvTimeIn),
rttSample(rttSampleIn),
ackDelay(ackDelayIn),
metadata(pkt.metadata),
lastAckedPacketInfo(pkt.lastAckedPacketInfo) {}
TimePoint rcvTime;
std::chrono::microseconds rttSample;
std::chrono::microseconds ackDelay;
const quic::OutstandingPacketMetadata metadata;
const folly::Optional<OutstandingPacket::LastAckedPacketInfo>
lastAckedPacketInfo;
};
struct PMTUBlackholeEvent {
explicit PMTUBlackholeEvent(
TimePoint blackholeTimeIn,
std::chrono::microseconds timeSinceLastNonSearchStateIn,
D6DMachineState lastNonSearchStateIn,
D6DMachineState currentStateIn,
uint64_t udpSendPacketLenIn,
uint64_t lastProbeSizeIn,
uint64_t blackholeDetectionWindowIn,
uint64_t blackholeDetectionThresholdIn,
const quic::OutstandingPacket& pkt)
: blackholeTime(blackholeTimeIn),
timeSinceLastNonSearchState(timeSinceLastNonSearchStateIn),
lastNonSearchState(lastNonSearchStateIn),
currentState(currentStateIn),
udpSendPacketLen(udpSendPacketLenIn),
lastProbeSize(lastProbeSizeIn),
blackholeDetectionWindow(blackholeDetectionWindowIn),
blackholeDetectionThreshold(blackholeDetectionThresholdIn),
triggeringPacketMetadata(pkt.metadata) {}
TimePoint blackholeTime;
// How long since last "stable" state
std::chrono::microseconds timeSinceLastNonSearchState;
D6DMachineState lastNonSearchState;
D6DMachineState currentState;
uint64_t udpSendPacketLen;
uint64_t lastProbeSize;
uint64_t blackholeDetectionWindow;
uint64_t blackholeDetectionThreshold;
// The metadata of the packet that triggerred blackhole signal
const quic::OutstandingPacketMetadata triggeringPacketMetadata;
};
struct PMTUUpperBoundEvent {
explicit PMTUUpperBoundEvent(
TimePoint upperBoundTimeIn,
std::chrono::microseconds timeSinceLastNonSearchStateIn,
D6DMachineState lastNonSearchStateIn,
uint64_t upperBoundPMTUIn,
uint64_t cumulativeProbesSentIn,
ProbeSizeRaiserType probeSizeRaiserTypeIn)
: upperBoundTime(upperBoundTimeIn),
timeSinceLastNonSearchState(timeSinceLastNonSearchStateIn),
lastNonSearchState(lastNonSearchStateIn),
upperBoundPMTU(upperBoundPMTUIn),
cumulativeProbesSent(cumulativeProbesSentIn),
probeSizeRaiserType(probeSizeRaiserTypeIn) {}
TimePoint upperBoundTime;
// How long it took to reach upperbound
std::chrono::microseconds timeSinceLastNonSearchState;
D6DMachineState lastNonSearchState;
uint64_t upperBoundPMTU;
uint64_t cumulativeProbesSent;
ProbeSizeRaiserType probeSizeRaiserType;
};
struct SpuriousLossEvent {
explicit SpuriousLossEvent(const TimePoint rcvTimeIn = Clock::now())
: rcvTime(rcvTimeIn) {}
bool hasPackets() {
return spuriousPackets.size() > 0;
}
void addSpuriousPacket(const quic::OutstandingPacket& pkt) {
spuriousPackets.emplace_back(
pkt.lossTimeoutDividend.hasValue(),
pkt.lossReorderDistance.hasValue(),
pkt);
}
const TimePoint rcvTime;
std::vector<LostPacket> spuriousPackets;
};
struct KnobFrameEvent {
explicit KnobFrameEvent(TimePoint rcvTimeIn, quic::KnobFrame knobFrame)
: rcvTime(rcvTimeIn), knobFrame(std::move(knobFrame)) {}
const TimePoint rcvTime;
const quic::KnobFrame knobFrame;
};
struct StreamEvent {
StreamEvent(
const StreamId id,
StreamInitiator initiator,
StreamDirectionality directionality)
: streamId(id),
streamInitiator(initiator),
streamDirectionality(directionality) {}
const StreamId streamId;
const StreamInitiator streamInitiator;
const StreamDirectionality streamDirectionality;
};
using StreamOpenEvent = StreamEvent;
using StreamCloseEvent = StreamEvent;
/**
* observerAttach() will be invoked when an observer is added.
*
* @param socket Socket where observer was installed.
*/
virtual void observerAttach(QuicSocket* /* socket */) noexcept {}
/**
* observerDetach() will be invoked if the observer is uninstalled prior
* to socket destruction.
*
* No further callbacks will be invoked after observerDetach().
*
* @param socket Socket where observer was uninstalled.
*/
virtual void observerDetach(QuicSocket* /* socket */) noexcept {}
/**
* destroy() will be invoked when the QuicSocket's destructor is invoked.
*
* No further callbacks will be invoked after destroy().
*
* @param socket Socket being destroyed.
*/
virtual void destroy(QuicSocket* /* socket */) noexcept {}
/**
* close() will be invoked when the socket is being closed.
*
* If the callback handler does not unsubscribe itself upon being called,
* then it may be called multiple times (e.g., by a call to close() by
* the application, and then again when closeNow() is called on
* destruction).
*
* @param socket Socket being closed.
* @param errorOpt Error information, if connection closed due to error.
*/
virtual void close(
QuicSocket* /* socket */,
const folly::Optional<
std::pair<QuicErrorCode, std::string>>& /* errorOpt */) noexcept {}
/**
* evbAttach() will be invoked when a new event base is attached to this
* socket. This will be called from the new event base's thread.
*
* @param socket Socket on which the new event base was attached.
* @param evb The new event base that is getting attached.
*/
virtual void evbAttach(
QuicSocket* /* socket */,
folly::EventBase* /* evb */) noexcept {}
/**
* evbDetach() will be invoked when an existing event base is detached
* from the socket. This will be called from the existing event base's thread.
*
* @param socket Socket on which the existing EVB is getting detached.
* @param evb The existing event base that is getting detached.
*/
virtual void evbDetach(
QuicSocket* /* socket */,
folly::EventBase* /* evb */) noexcept {}
/**
* startWritingFromAppLimited() is invoked when the socket is currenty
* app rate limited and is being given an opportunity to write packets.
*
* @param socket Socket that is starting to write from an app limited state.
* @param event AppLimitedEvent with details.
*/
virtual void startWritingFromAppLimited(
QuicSocket* /* socket */,
const AppLimitedEvent& /* event */) {}
/**
* packetsWritten() is invoked when the socket writes packets to the wire.
*
* @param socket Socket for which packets were written to the wire.
* @param event PacketsWrittenEvent with details.
*/
virtual void packetsWritten(
QuicSocket* /* socket */,
const PacketsWrittenEvent& /* event */) {}
/**
* appRateLimited() is invoked when the socket becomes app rate limited.
*
* @param socket Socket that has become application rate limited.
* @param event AppLimitedEvent with details.
*/
virtual void appRateLimited(
QuicSocket* /* socket */,
const AppLimitedEvent& /* event */) {}
/**
* acksProcessed() is invoked when ACKs from remote are processed.
*
* @param socket Socket when the callback is processed.
* @param event Event with information about ACKs processed.
*/
virtual void acksProcessed(
QuicSocket*, /* socket */
const struct AcksProcessedEvent& /* event */) {}
/**
* packetLossDetected() is invoked when a packet loss is detected.
*
* @param socket Socket for which packet loss was detected.
* @param event LossEvent with details.
*/
virtual void packetLossDetected(
QuicSocket*, /* socket */
const LossEvent& /* event */) {}
/**
* rttSampleGenerated() is invoked when a RTT sample is made.
*
* @param socket Socket for which an RTT sample was generated.
* @param event PacketRTTEvent with details.
*/
virtual void rttSampleGenerated(
QuicSocket*, /* socket */
const PacketRTT& /* event */) {}
/**
* pmtuProbingStarted() is invoked when server starts d6d.
*
* @param socket Socket when the callback is processed.
*/
virtual void pmtuProbingStarted(QuicSocket* /* socket */) {}
/**
* pmtuBlackholeDetected() is invoked when a PMTU blackhole is detected.
*
* @param pmtuBlackholeEvent const reference to the PMTU blackhole event
*/
virtual void pmtuBlackholeDetected(
QuicSocket*, /* socket */
const PMTUBlackholeEvent& /* event */) {}
/**
* pmtuUpperBoundDetected() is invoked when a PMTU upperbound is detected.
*
* @param pmtuUpperBoundEvent const reference to the PMTU upperbound event
*/
virtual void pmtuUpperBoundDetected(
QuicSocket*, /* socket */
const PMTUUpperBoundEvent& /* event */) {}
/**
* spuriousLossDetected() is invoked when an ACK arrives for a packet that is
* declared lost
*
* @param socket Socket when the callback is processed.
* @param packet const reference to the lost packet.
*/
virtual void spuriousLossDetected(
QuicSocket*, /* socket */
const SpuriousLossEvent& /* event */) {}
/**
* knobFrameReceived() is invoked when a knob frame is received.
*
* @param socket Socket when the callback is processed.
* @param event const reference to the KnobFrameEvent.
*/
virtual void knobFrameReceived(
QuicSocket*, /* socket */
const KnobFrameEvent& /* event */) {}
/**
* streamOpened() is invoked when a new stream is opened.
*
* @param socket Socket associated with the event.
* @param event Event containing details.
*/
virtual void streamOpened(
QuicSocket*, /* socket */
const StreamOpenEvent& /* event */) {}
/**
* streamClosed() is invoked when a stream is closed.
*
* @param socket Socket associated with the event.
* @param event Event containing details.
*/
virtual void streamClosed(
QuicSocket*, /* socket */
const StreamCloseEvent& /* event */) {}
protected:
// observer configuration; cannot be changed post instantiation
const Config observerConfig_;
};
// Container for instrumentation observers.
// Avoids heap allocation for up to 2 observers being installed.
using ObserverVec = SmallVec<Observer*, 2>;
} // namespace quic