diff --git a/quic/api/Observer.h b/quic/api/Observer.h new file mode 100644 index 000000000..8127f3cb6 --- /dev/null +++ b/quic/api/Observer.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) Facebook, Inc. and its 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 +#include +#include + +namespace quic { +class QuicSocket; +/** + * ===== Instrumentation Observer API ===== + */ + +/** + * Observer of socket instrumentation events. + */ +class InstrumentationObserver { + public: + 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 ObserverLossEvent { + explicit ObserverLossEvent(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 lostPackets; + }; + virtual ~InstrumentationObserver() = default; + + /** + * observerDetach() will be invoked when the observer is uninstalled. + * + * No further callbacks will be invoked after observerDetach(). + * + * @param socket Socket where observer was uninstalled. + */ + virtual void observerDetach(QuicSocket* /* socket */) noexcept = 0; + + /** + * appRateLimited() is invoked when the socket is app rate limited. + * + * @param socket Socket that has become application rate limited. + */ + virtual void appRateLimited(QuicSocket* /* socket */) {} + + /** + * packetLossDetected() is invoked when a packet loss is detected. + * + * @param packet const reference to the packet that was determined to be + * lost. + */ + virtual void packetLossDetected( + const struct ObserverLossEvent& /* lossEvent */) {} +}; + +// Container for instrumentation observers. +// Avoids heap allocation for up to 2 observers being installed. +using InstrumentationObserverVec = SmallVec; + +/** + * ===== Lifecycle Observer API ===== + */ + +/** + * Observer of socket lifecycle events. + */ +class LifecycleObserver { + public: + virtual ~LifecycleObserver() = default; + + /** + * observerAttach() will be invoked when an observer is added. + * + * @param socket Socket where observer was installed. + */ + virtual void observerAttach(QuicSocket* /* socket */) noexcept = 0; + + /** + * 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 = 0; + + /** + * 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 = 0; + + /** + * 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>& /* errorOpt */) noexcept = 0; +}; + +} // namespace quic diff --git a/quic/api/QuicSocket.h b/quic/api/QuicSocket.h index dcbd93346..e34b820fa 100644 --- a/quic/api/QuicSocket.h +++ b/quic/api/QuicSocket.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -1093,61 +1094,6 @@ class QuicSocket { */ virtual void setCongestionControl(CongestionControlType type) = 0; - /** - * ===== Lifecycle Observer API ===== - */ - - /** - * Observer of socket lifecycle events. - */ - class LifecycleObserver { - public: - virtual ~LifecycleObserver() = default; - - /** - * observerAttach() will be invoked when an observer is added. - * - * @param socket Socket where observer was installed. - */ - virtual void observerAttach(QuicSocket* /* socket */) noexcept = 0; - - /** - * 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 = 0; - - /** - * 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 = 0; - - /** - * 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>& /* errorOpt */) noexcept = 0; - }; - // Container for lifecycle observers. // Avoids heap allocation for up to 2 observers being installed. using LifecycleObserverVec = SmallVec; @@ -1181,38 +1127,6 @@ class QuicSocket { FOLLY_NODISCARD virtual const LifecycleObserverVec& getLifecycleObservers() const = 0; - /** - * ===== Instrumentation Observer API ===== - */ - - /** - * Observer of socket instrumentation events. - */ - class InstrumentationObserver { - public: - virtual ~InstrumentationObserver() = default; - - /** - * observerDetach() will be invoked when the observer is uninstalled. - * - * No further callbacks will be invoked after observerDetach(). - * - * @param socket Socket where observer was uninstalled. - */ - virtual void observerDetach(QuicSocket* /* socket */) noexcept = 0; - - /** - * appRateLimited() is invoked when the socket is app rate limited. - * - * @param socket Socket that has become application rate limited. - */ - virtual void appRateLimited(QuicSocket* /* socket */) {} - }; - - // Container for instrumentation observers. - // Avoids heap allocation for up to 2 observers being installed. - using InstrumentationObserverVec = SmallVec; - /** * Adds a instrumentation observer. * diff --git a/quic/api/QuicTransportBase.cpp b/quic/api/QuicTransportBase.cpp index e598170c1..80bc88cfa 100644 --- a/quic/api/QuicTransportBase.cpp +++ b/quic/api/QuicTransportBase.cpp @@ -249,10 +249,10 @@ void QuicTransportBase::closeImpl( for (const auto& cb : lifecycleObservers_) { cb->close(this, errorCode); } - for (const auto& cb : instrumentationObservers_) { + for (const auto& cb : conn_->instrumentationObservers_) { cb->observerDetach(this); } - instrumentationObservers_.clear(); + conn_->instrumentationObservers_.clear(); if (closeState_ == CloseState::CLOSED) { return; @@ -1645,6 +1645,12 @@ void QuicTransportBase::processCallbacksAfterNetworkData() { } } + // to call any callbacks added for observers + for (const auto& callback : conn_->pendingCallbacks) { + callback(); + } + conn_->pendingCallbacks.clear(); + handlePingCallback(); if (closeState_ != CloseState::OPEN) { return; @@ -2596,29 +2602,30 @@ QuicTransportBase::getLifecycleObservers() const { void QuicTransportBase::addInstrumentationObserver( InstrumentationObserver* observer) { - instrumentationObservers_.push_back(CHECK_NOTNULL(observer)); + conn_->instrumentationObservers_.push_back(CHECK_NOTNULL(observer)); } bool QuicTransportBase::removeInstrumentationObserver( InstrumentationObserver* observer) { const auto eraseIt = std::remove( - instrumentationObservers_.begin(), - instrumentationObservers_.end(), + conn_->instrumentationObservers_.begin(), + conn_->instrumentationObservers_.end(), observer); - if (eraseIt == instrumentationObservers_.end()) { + if (eraseIt == conn_->instrumentationObservers_.end()) { return false; } - for (auto it = eraseIt; it != instrumentationObservers_.end(); it++) { + for (auto it = eraseIt; it != conn_->instrumentationObservers_.end(); it++) { (*it)->observerDetach(this); } - instrumentationObservers_.erase(eraseIt, instrumentationObservers_.end()); + conn_->instrumentationObservers_.erase( + eraseIt, conn_->instrumentationObservers_.end()); return true; } -const QuicTransportBase::InstrumentationObserverVec& +const InstrumentationObserverVec& QuicTransportBase::getInstrumentationObservers() const { - return instrumentationObservers_; + return conn_->instrumentationObservers_; } void QuicTransportBase::writeSocketData() { @@ -2669,7 +2676,7 @@ void QuicTransportBase::writeSocketData() { conn_->congestionController->setAppLimited(); // notify via connection call and any instrumentation callbacks connCallback_->onAppRateLimited(); - for (const auto& cb : instrumentationObservers_) { + for (const auto& cb : conn_->instrumentationObservers_) { cb->appRateLimited(this); } } diff --git a/quic/api/test/Mocks.h b/quic/api/test/Mocks.h index b456f7cba..8833a9331 100644 --- a/quic/api/test/Mocks.h +++ b/quic/api/test/Mocks.h @@ -307,7 +307,7 @@ class MockLoopDetectorCallback : public LoopDetectorCallback { MOCK_METHOD2(onSuspiciousReadLoops, void(uint64_t, NoReadReason)); }; -class MockLifecycleObserver : public QuicSocket::LifecycleObserver { +class MockLifecycleObserver : public LifecycleObserver { public: GMOCK_METHOD1_(, noexcept, , observerAttach, void(QuicSocket*)); GMOCK_METHOD1_(, noexcept, , observerDetach, void(QuicSocket*)); @@ -322,10 +322,26 @@ class MockLifecycleObserver : public QuicSocket::LifecycleObserver { const folly::Optional>&)); }; -class MockInstrumentationObserver : public QuicSocket::InstrumentationObserver { +class MockInstrumentationObserver : public InstrumentationObserver { public: GMOCK_METHOD1_(, noexcept, , observerDetach, void(QuicSocket*)); GMOCK_METHOD1_(, noexcept, , appRateLimited, void(QuicSocket*)); + GMOCK_METHOD1_( + , + noexcept, + , + packetLossDetected, + void(const ObserverLossEvent&)); + + static auto getLossPacketMatcher(bool reorderLoss, bool timeoutLoss) { + return AllOf( + testing::Field( + &InstrumentationObserver::LostPacket::lostByReorderThreshold, + testing::Eq(reorderLoss)), + testing::Field( + &InstrumentationObserver::LostPacket::lostByTimeout, + testing::Eq(timeoutLoss))); + } }; inline std::ostream& operator<<(std::ostream& os, const MockQuicTransport&) { diff --git a/quic/api/test/QuicTransportBaseTest.cpp b/quic/api/test/QuicTransportBaseTest.cpp index 9b16ec500..24af22d80 100644 --- a/quic/api/test/QuicTransportBaseTest.cpp +++ b/quic/api/test/QuicTransportBaseTest.cpp @@ -423,6 +423,10 @@ class TestQuicTransport writeSocketData(); } + void invokeProcessCallbacksAfterNetworkData() { + processCallbacksAfterNetworkData(); + } + QuicServerConnectionState* transportConn; std::unique_ptr aead; std::unique_ptr headerCipher; @@ -3652,5 +3656,27 @@ TEST_F( Mock::VerifyAndClearExpectations(cb2.get()); } +TEST_F(QuicTransportImplTest, ImplementationObserverCallbacksDeleted) { + auto noopCallback = [] {}; + transport->transportConn->pendingCallbacks.emplace_back(noopCallback); + EXPECT_EQ(1, size(transport->transportConn->pendingCallbacks)); + transport->invokeProcessCallbacksAfterNetworkData(); + EXPECT_EQ(0, size(transport->transportConn->pendingCallbacks)); +} + +TEST_F(QuicTransportImplTest, ImplementationObserverCallbacksInvoked) { + uint32_t callbacksInvoked = 0; + auto countingCallback = [&]() { callbacksInvoked++; }; + + for (int i = 0; i < 2; i++) { + transport->transportConn->pendingCallbacks.emplace_back(countingCallback); + } + EXPECT_EQ(2, size(transport->transportConn->pendingCallbacks)); + transport->invokeProcessCallbacksAfterNetworkData(); + + EXPECT_EQ(2, callbacksInvoked); + EXPECT_EQ(0, size(transport->transportConn->pendingCallbacks)); +} + } // namespace test } // namespace quic diff --git a/quic/loss/QuicLossFunctions.h b/quic/loss/QuicLossFunctions.h index 7a75dbaa5..4c4099a06 100644 --- a/quic/loss/QuicLossFunctions.h +++ b/quic/loss/QuicLossFunctions.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include #include @@ -204,6 +205,7 @@ folly::Optional detectLossPackets( << " delayUntilLost=" << delayUntilLost.count() << "us" << " " << conn; CongestionController::LossEvent lossEvent(lossTime); + InstrumentationObserver::ObserverLossEvent observerLossEvent(lossTime); // Note that time based loss detection is also within the same PNSpace. auto iter = getFirstOutstandingPacket(conn, pnSpace); bool shouldSetTimer = false; @@ -218,16 +220,19 @@ folly::Optional detectLossPackets( iter++; continue; } - bool lost = (lossTime - pkt.time) > delayUntilLost; - lost = lost || + bool lostByTimeout = (lossTime - pkt.time) > delayUntilLost; + bool lostByReorder = (*largestAcked - currentPacketNum) > conn.lossState.reorderingThreshold; - if (!lost) { + + if (!(lostByTimeout || lostByReorder)) { // We can exit early here because if packet N doesn't meet the // threshold, then packet N + 1 will not either. shouldSetTimer = true; break; } lossEvent.addLostPacket(pkt); + observerLossEvent.addLostPacket(lostByTimeout, lostByReorder, pkt); + if (pkt.associatedEvent) { DCHECK_GT(conn.outstandings.clonedPacketsCount, 0); --conn.outstandings.clonedPacketsCount; @@ -260,6 +265,15 @@ folly::Optional detectLossPackets( iter++; } + // if there are observers, enqueue a function to call it + if (observerLossEvent.hasPackets()) { + for (const auto& observer : conn.instrumentationObservers_) { + conn.pendingCallbacks.emplace_back([observer, observerLossEvent] { + observer->packetLossDetected(observerLossEvent); + }); + } + } + auto earliest = getFirstOutstandingPacket(conn, pnSpace); for (; earliest != conn.outstandings.packets.end(); earliest = getNextOutstandingPacket(conn, pnSpace, earliest + 1)) { diff --git a/quic/loss/test/QuicLossFunctionsTest.cpp b/quic/loss/test/QuicLossFunctionsTest.cpp index 237874318..b4f371582 100644 --- a/quic/loss/test/QuicLossFunctionsTest.cpp +++ b/quic/loss/test/QuicLossFunctionsTest.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -121,6 +122,11 @@ class QuicLossFunctionsTest : public TestWithParam { MockLossTimeout timeout; std::unique_ptr transportInfoCb_; std::unique_ptr connIdAlgo_; + + auto getLossPacketMatcher(bool lossByReorder, bool lossByTimeout) { + return MockInstrumentationObserver::getLossPacketMatcher( + lossByReorder, lossByTimeout); + } }; auto testingLossMarkFunc(std::vector& lostPackets) { @@ -1592,6 +1598,210 @@ TEST_F(QuicLossFunctionsTest, PersistentCongestion) { EXPECT_FALSE(isPersistentCongestion(*conn, currentTime - 100us, currentTime)); } +TEST_F(QuicLossFunctionsTest, TestReorderLossObserverCallback) { + auto ib = MockInstrumentationObserver(); + auto conn = createConn(); + // Register 1 instrumentation observer + conn->instrumentationObservers_.emplace_back(&ib); + auto noopLossVisitor = [](auto&, auto&, bool) {}; + + PacketNum largestSent = 0; + for (int i = 0; i < 7; ++i) { + largestSent = + sendPacket(*conn, TimePoint(i * 10ms), folly::none, PacketType::OneRtt); + } + // Some packets are already acked + conn->outstandings.packets.erase( + getFirstOutstandingPacket(*conn, PacketNumberSpace::AppData) + 2, + getFirstOutstandingPacket(*conn, PacketNumberSpace::AppData) + 5); + + // setting a very low reordering threshold to force loss by reorder + conn->lossState.reorderingThreshold = 1; + // setting time out parameters higher than the time at which detectLossPackets + // is called to make sure there are no losses by timeout + conn->lossState.srtt = 400ms; + conn->lossState.lrtt = 350ms; + conn->transportSettings.timeReorderingThreshDividend = 1.0; + conn->transportSettings.timeReorderingThreshDivisor = 1.0; + TimePoint checkTime = TimePoint(200ms); + + detectLossPackets( + *conn, + largestSent + 1, + noopLossVisitor, + checkTime, + PacketNumberSpace::AppData); + + // expecting 1 callback to be stacked + EXPECT_EQ(1, size(conn->pendingCallbacks)); + + // Out of 1, 2, 3, 4, 5, 6, 7 -- we deleted (acked) 3,4,5. + // 1, 2 and 6 are "lost" due to reodering. None lost due to timeout + EXPECT_CALL( + ib, + packetLossDetected(Field( + &InstrumentationObserver::ObserverLossEvent::lostPackets, + UnorderedElementsAre( + getLossPacketMatcher(true, false), + getLossPacketMatcher(true, false), + getLossPacketMatcher(true, false))))) + .Times(1); + + for (auto& callback : conn->pendingCallbacks) { + callback(); + } +} + +TEST_F(QuicLossFunctionsTest, TestTimeoutLossObserverCallback) { + auto ib = MockInstrumentationObserver(); + auto conn = createConn(); + // Register 1 instrumentation observer + conn->instrumentationObservers_.emplace_back(&ib); + auto noopLossVisitor = [](auto&, auto&, bool) {}; + + PacketNum largestSent = 0; + + // send 7 packets + for (int i = 0; i < 7; ++i) { + largestSent = + sendPacket(*conn, TimePoint(i * 10ms), folly::none, PacketType::OneRtt); + } + + // setting a very high reordering threshold to force loss by timeout only + conn->lossState.reorderingThreshold = 100; + // setting time out parameters lower than the time at which detectLossPackets + // is called to make sure all packets timeout + conn->lossState.srtt = 400ms; + conn->lossState.lrtt = 350ms; + conn->transportSettings.timeReorderingThreshDividend = 1.0; + conn->transportSettings.timeReorderingThreshDivisor = 1.0; + TimePoint checkTime = TimePoint(500ms); + + detectLossPackets( + *conn, + largestSent + 1, + noopLossVisitor, + checkTime, + PacketNumberSpace::AppData); + + // expecting 1 callback to be stacked + EXPECT_EQ(1, size(conn->pendingCallbacks)); + + // expecting all packets to be lost due to timeout + EXPECT_CALL( + ib, + packetLossDetected(Field( + &InstrumentationObserver::ObserverLossEvent::lostPackets, + UnorderedElementsAre( + getLossPacketMatcher(false, true), + getLossPacketMatcher(false, true), + getLossPacketMatcher(false, true), + getLossPacketMatcher(false, true), + getLossPacketMatcher(false, true), + getLossPacketMatcher(false, true), + getLossPacketMatcher(false, true))))) + .Times(1); + + for (auto& callback : conn->pendingCallbacks) { + callback(); + } +} + +TEST_F(QuicLossFunctionsTest, TestTimeoutAndReorderLossObserverCallback) { + auto ib = MockInstrumentationObserver(); + auto conn = createConn(); + // Register 1 instrumentation observer + conn->instrumentationObservers_.emplace_back(&ib); + auto noopLossVisitor = [](auto&, auto&, bool) {}; + + PacketNum largestSent = 0; + for (int i = 0; i < 7; ++i) { + largestSent = + sendPacket(*conn, TimePoint(i * 10ms), folly::none, PacketType::OneRtt); + } + + // Some packets are already acked + conn->outstandings.packets.erase( + getFirstOutstandingPacket(*conn, PacketNumberSpace::AppData) + 2, + getFirstOutstandingPacket(*conn, PacketNumberSpace::AppData) + 5); + + // setting a low reorder threshold + conn->lossState.reorderingThreshold = 1; + + // setting time out parameters lower than the time at which detectLossPackets + // is called to make sure all packets timeout + conn->lossState.srtt = 400ms; + conn->lossState.lrtt = 350ms; + conn->transportSettings.timeReorderingThreshDividend = 1.0; + conn->transportSettings.timeReorderingThreshDivisor = 1.0; + TimePoint checkTime = TimePoint(500ms); + + detectLossPackets( + *conn, + largestSent + 1, + noopLossVisitor, + checkTime, + PacketNumberSpace::AppData); + + // expecting 1 callback to be stacked + EXPECT_EQ(1, size(conn->pendingCallbacks)); + + // Out of 1, 2, 3, 4, 5, 6, 7 -- we deleted (acked) 3,4,5. + // 1, 2, 6 are lost due to reodering and timeout. + // 7 just timed out + EXPECT_CALL( + ib, + packetLossDetected(Field( + &InstrumentationObserver::ObserverLossEvent::lostPackets, + UnorderedElementsAre( + getLossPacketMatcher(true, true), + getLossPacketMatcher(true, true), + getLossPacketMatcher(true, true), + getLossPacketMatcher(false, true))))) + .Times(1); + + for (auto& callback : conn->pendingCallbacks) { + callback(); + } +} + +TEST_F(QuicLossFunctionsTest, TestNoInstrumentationObserverCallback) { + auto conn = createConn(); + auto noopLossVisitor = [](auto&, auto&, bool) {}; + + PacketNum largestSent = 0; + for (int i = 0; i < 7; ++i) { + largestSent = + sendPacket(*conn, TimePoint(i * 10ms), folly::none, PacketType::OneRtt); + } + + // Some packets are already acked + conn->outstandings.packets.erase( + getFirstOutstandingPacket(*conn, PacketNumberSpace::AppData) + 2, + getFirstOutstandingPacket(*conn, PacketNumberSpace::AppData) + 5); + + // setting a low reorder threshold + conn->lossState.reorderingThreshold = 1; + + // setting time out parameters lower than the time at which detectLossPackets + // is called to make sure all packets timeout + conn->lossState.srtt = 400ms; + conn->lossState.lrtt = 350ms; + conn->transportSettings.timeReorderingThreshDividend = 1.0; + conn->transportSettings.timeReorderingThreshDivisor = 1.0; + TimePoint checkTime = TimePoint(500ms); + + detectLossPackets( + *conn, + largestSent + 1, + noopLossVisitor, + checkTime, + PacketNumberSpace::AppData); + + // expecting 0 callbacks to be queued + EXPECT_EQ(0, size(conn->pendingCallbacks)); +} + INSTANTIATE_TEST_CASE_P( QuicLossFunctionsTests, QuicLossFunctionsTest, diff --git a/quic/state/OutstandingPacket.h b/quic/state/OutstandingPacket.h new file mode 100644 index 000000000..38b039e36 --- /dev/null +++ b/quic/state/OutstandingPacket.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) Facebook, Inc. and its 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 +#include + +namespace quic { +// Data structure to represent outstanding retransmittable packets +struct OutstandingPacket { + // Structure representing the frames that are outstanding including the header + // that was sent. + RegularQuicWritePacket packet; + // Time that the packet was sent. + TimePoint time; + // Size of the packet sent on the wire. + uint32_t encodedSize; + // Whether this packet has any data from stream 0 + bool isHandshake; + // Total sent bytes on this connection including this packet itself when this + // packet is sent. + uint64_t totalBytesSent; + // Information regarding the last acked packet on this connection when this + // packet is sent. + struct LastAckedPacketInfo { + TimePoint sentTime; + TimePoint ackTime; + // Total sent bytes on this connection when the last acked packet is acked. + uint64_t totalBytesSent; + // Total acked bytes on this connection when last acked packet is acked, + // including the last acked packet. + uint64_t totalBytesAcked; + + LastAckedPacketInfo( + TimePoint sentTimeIn, + TimePoint ackTimeIn, + uint64_t totalBytesSentIn, + uint64_t totalBytesAckedIn) + : sentTime(sentTimeIn), + ackTime(ackTimeIn), + totalBytesSent(totalBytesSentIn), + totalBytesAcked(totalBytesAckedIn) {} + }; + folly::Optional lastAckedPacketInfo; + + // PacketEvent associated with this OutstandingPacket. This will be a + // folly::none if the packet isn't a clone and hasn't been cloned. + folly::Optional associatedEvent; + + /** + * Whether the packet is sent when congestion controller is in app-limited + * state. + */ + bool isAppLimited{false}; + + // True if spurious loss detection is enabled and this packet was declared + // lost. + bool declaredLost{false}; + + OutstandingPacket( + RegularQuicWritePacket packetIn, + TimePoint timeIn, + uint32_t encodedSizeIn, + bool isHandshakeIn, + uint64_t totalBytesSentIn) + : packet(std::move(packetIn)), + time(std::move(timeIn)), + encodedSize(encodedSizeIn), + isHandshake(isHandshakeIn), + totalBytesSent(totalBytesSentIn) {} +}; +} // namespace quic diff --git a/quic/state/StateData.h b/quic/state/StateData.h index e42269701..cdf1e5279 100644 --- a/quic/state/StateData.h +++ b/quic/state/StateData.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -103,70 +105,6 @@ struct NetworkDataSingle { } }; -// Data structure to represent outstanding retransmittable packets -struct OutstandingPacket { - // Structure representing the frames that are outstanding including the header - // that was sent. - RegularQuicWritePacket packet; - // Time that the packet was sent. - TimePoint time; - // Size of the packet sent on the wire. - uint32_t encodedSize; - // Whether this packet has any data from stream 0 - bool isHandshake; - // Total sent bytes on this connection including this packet itself when this - // packet is sent. - uint64_t totalBytesSent; - // Information regarding the last acked packet on this connection when this - // packet is sent. - struct LastAckedPacketInfo { - TimePoint sentTime; - TimePoint ackTime; - // Total sent bytes on this connection when the last acked packet is acked. - uint64_t totalBytesSent; - // Total acked bytes on this connection when last acked packet is acked, - // including the last acked packet. - uint64_t totalBytesAcked; - - LastAckedPacketInfo( - TimePoint sentTimeIn, - TimePoint ackTimeIn, - uint64_t totalBytesSentIn, - uint64_t totalBytesAckedIn) - : sentTime(sentTimeIn), - ackTime(ackTimeIn), - totalBytesSent(totalBytesSentIn), - totalBytesAcked(totalBytesAckedIn) {} - }; - folly::Optional lastAckedPacketInfo; - - // PacketEvent associated with this OutstandingPacket. This will be a - // folly::none if the packet isn't a clone and hasn't been cloned. - folly::Optional associatedEvent; - - /** - * Whether the packet is sent when congestion controller is in app-limited - * state. - */ - bool isAppLimited{false}; - - // True if spurious loss detection is enabled and this packet was declared - // lost. - bool declaredLost{false}; - - OutstandingPacket( - RegularQuicWritePacket packetIn, - TimePoint timeIn, - uint32_t encodedSizeIn, - bool isHandshakeIn, - uint64_t totalBytesSentIn) - : packet(std::move(packetIn)), - time(std::move(timeIn)), - encodedSize(encodedSizeIn), - isHandshake(isHandshakeIn), - totalBytesSent(totalBytesSentIn) {} -}; - struct OutstandingsInfo { // Sent packets which have not been acked. These are sorted by PacketNum. std::deque packets; @@ -852,6 +790,12 @@ struct QuicConnectionStateBase : public folly::DelayedDestruction { * Return true if replacement succeeds. */ bool retireAndSwitchPeerConnectionIds(); + + // instrumentation observers + InstrumentationObserverVec instrumentationObservers_; + + // queue of functions to be called in processCallbacksAfterNetworkData + std::vector> pendingCallbacks; }; std::ostream& operator<<(std::ostream& os, const QuicConnectionStateBase& st);