1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-04-18 17:24:03 +03:00
Ilango Purushothaman e1fd9c7880 Create OutstandingPacket wrapper
Summary:
To get reliable packet destruction events, created a `OutstandingPacketWrapper` wrapper that wraps the current `OutstandingPacket` class, so callback functions can be added to the wrapper instead of the underlying object itself.

#### Why do we need this wrapper?

`std::deque::erase` does not guarantee that appropriate object destructors will be called (only that the number of destructions = number of objects erased). This is a real problem as packet destruction events are then no longer reliable (OutstandingPacket object is wrong!). The wrapper class handles this condition by detecting it in the move assignment constructor (called during erase) and calls the appropriate packet destruction callback before packets are moved. If we did the same fix in the old OutstandingPacket, we have to make sure the callback is called before all the fields of OutstandingPacket are moved - this is not scaleable. Hence a wrapper with the underlying object and a destruction callback function.
I also disabled copy construction for OutstandingPacket (otherwise we will get duplicate OnPacketDestroyed callbacks and cannot track packets reliably). Removing packet copies also improves performance. Some code changes (in tests mostly) are mostly in service of this particular change.

Reviewed By: bschlinker

Differential Revision: D43896148

fbshipit-source-id: c295d3c4dba2368aa66f06df5fc82b473a03fb4d
2023-03-15 03:10:18 -07:00

126 lines
4.7 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/congestion_control/Bandwidth.h>
#include <quic/congestion_control/CongestionController.h>
#include <quic/congestion_control/QuicCubic.h>
#include <quic/server/state/ServerStateMachine.h>
#include <quic/state/AckEvent.h>
#include <quic/state/StateData.h>
#ifdef CCP_ENABLED
#include <ccp/ccp.h>
#endif
#include <limits>
namespace quic {
/**
* The class defines CCP as a congestion control "algorithm", but it really acts
* as a proxy layer between the datapath (mvfst) and CCP. Each instance of this
* class corresponds to a separate connection that is using CCP for congestion
* control.
*
* From the perspective of this class, the behavior is very simple:
* - On each ack or loss, it updates some statistics about the connection (rtt
* etc.)
* - Once in a while, after updating it will batch the last few updates and send
* them to CCP (via IPC).
* - Asynchronously, at any time CCP may send a message (via IPC) telling this
* connection to use a new cwnd or rate. CCPReader receives these messages,
* looks up the correct instance of QuicCCP for that connection, and calls
* setCongestionWindow or setPacingRate. This class does not contain any logic
* for updating the cwnd (or rate) directly, they only change when directed by
* CCP.
*
* The low-level functionality, such as batching updates, serializing them,
* sending IPC, etc. is handled by libccp, which is a helper library provided by
* the CCP authors.
*
* If, for whatever reason, mvfst has not received any responses from CCP for a
* while, we go into "fallback" mode and use cubic instead, by simply proxying
* the acks, losses, and cwnd updates to an internal instance of QuicCubic. If
* connection is re-established with CCP, we leave fallback mode and pick up the
* cwnd where cubic left off.
*
*/
class CCP : public CongestionController {
public:
explicit CCP(QuicConnectionStateBase& conn);
~CCP() override;
void onRemoveBytesFromInflight(uint64_t) override;
void onPacketSent(const OutstandingPacketWrapper& packet) override;
void onPacketAckOrLoss(
const AckEvent* FOLLY_NULLABLE,
const LossEvent* FOLLY_NULLABLE) override;
void onPacketAckOrLoss(
folly::Optional<AckEvent> ack,
folly::Optional<LossEvent> loss) {
onPacketAckOrLoss(ack.get_pointer(), loss.get_pointer());
}
FOLLY_NODISCARD uint64_t getWritableBytes() const noexcept override;
FOLLY_NODISCARD uint64_t getCongestionWindow() const noexcept override;
// Called (indirectly) by CCP when it wants to update the cwnd for this
// connection.
void setCongestionWindow(uint64_t cwnd) noexcept;
void setAppIdle(bool, TimePoint) noexcept override;
void setAppLimited() override;
FOLLY_NODISCARD CongestionControlType type() const noexcept override;
FOLLY_NODISCARD uint64_t getBytesInFlight() const noexcept;
// Called indirectly by CCP when it wants to update the pacing rate for this
// connection.
void setPacingRate(uint64_t rate) noexcept;
FOLLY_NODISCARD bool isAppLimited() const noexcept override;
void getStats(CongestionControllerStats& /*stats*/) const override {}
private:
void onLossEvent(const LossEvent&);
void onAckEvent(const AckEvent&);
// Fallback to in-datapath congestion control impl (eg. because ccp not
// responding)
void fallback();
// Go back to using CCP after a period of fallback
void restoreAfterFallback();
private:
QuicServerConnectionState& conn_;
// The current cwnd for this connection when we are not in fallback mode.
// When we are in fallback mode, we instead use
// fallbackCC_->getCongestionWindow().
uint64_t cwndBytes_;
// The current pacing rate for this connection when we are not in fallback
// mode. When we are in fallback mode, the pacing rate is set by fallbackCC_.
uint64_t pacingRate_{0};
// The per-connection state needed by libccp, initialized by calling
// libccp::ccp_connection_start in the constructor.
struct ccp_connection* ccp_conn_{nullptr};
// The global (per QuicServerWorker) state needed by libccp, retrieved from
// the connection's corresponding QuicServerConnectionState.
struct ccp_datapath* datapath_{nullptr};
// Used to help ensure we don't count the same loss event multiple times.
folly::Optional<TimePoint> endOfRecovery_;
// Current estimate of the send and ack rate, two of the basic statistics sent
// to CCP.
Bandwidth sendRate_, ackRate_;
// Whether or not we are in fallback mode.
bool inFallback_;
// An instance of QuicCubic to use during fallback mode.
Cubic fallbackCC_;
};
} // namespace quic