mirror of
https://github.com/facebookincubator/mvfst.git
synced 2025-08-08 09:42:06 +03:00
Separate app-limited and app-idle
Summary: Current app-limited is defined as the connection doesn't have a no non-control stream. I'm changing this to be app-idle. And app-limited will be changed to a state in connection where app isn't sending bytes fast enough to keep congestion controller probing higher bandwidth. Reviewed By: mjoras Differential Revision: D15456199 fbshipit-source-id: e084bc133284ae37ee6da8ebb3c12f505011521e
This commit is contained in:
committed by
Facebook Github Bot
parent
41a6b82538
commit
1dbe39fd76
@@ -2127,6 +2127,17 @@ void QuicTransportBase::writeSocketData() {
|
|||||||
setIdleTimer();
|
setIdleTimer();
|
||||||
conn_->receivedNewPacketBeforeWrite = false;
|
conn_->receivedNewPacketBeforeWrite = false;
|
||||||
}
|
}
|
||||||
|
// Check if we are app-limited after finish this round of sending
|
||||||
|
auto currentSendBufLen = conn_->flowControlState.sumCurStreamBufferLen;
|
||||||
|
auto lossBufferEmpty = !conn_->streamManager->hasLoss() &&
|
||||||
|
conn_->cryptoState->initialStream.lossBuffer.empty() &&
|
||||||
|
conn_->cryptoState->handshakeStream.lossBuffer.empty() &&
|
||||||
|
conn_->cryptoState->oneRttStream.lossBuffer.empty();
|
||||||
|
if (conn_->congestionController &&
|
||||||
|
currentSendBufLen < conn_->udpSendPacketLen && lossBufferEmpty &&
|
||||||
|
conn_->congestionController->getWritableBytes()) {
|
||||||
|
conn_->congestionController->setAppLimited();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Writing data could write out an ack which could cause us to cancel
|
// Writing data could write out an ack which could cause us to cancel
|
||||||
|
@@ -366,7 +366,8 @@ void updateConnection(
|
|||||||
packetNum,
|
packetNum,
|
||||||
(uint64_t)encodedSize,
|
(uint64_t)encodedSize,
|
||||||
(int)isHandshake,
|
(int)isHandshake,
|
||||||
(int)pureAck);
|
(int)pureAck,
|
||||||
|
pkt.isAppLimited);
|
||||||
conn.lossState.largestSent = std::max(conn.lossState.largestSent, packetNum);
|
conn.lossState.largestSent = std::max(conn.lossState.largestSent, packetNum);
|
||||||
if (conn.congestionController && !pureAck) {
|
if (conn.congestionController && !pureAck) {
|
||||||
conn.congestionController->onPacketSent(pkt);
|
conn.congestionController->onPacketSent(pkt);
|
||||||
|
@@ -1467,26 +1467,26 @@ INSTANTIATE_TEST_CASE_P(
|
|||||||
QuicTransportImplTestUniBidi,
|
QuicTransportImplTestUniBidi,
|
||||||
Values(true, false));
|
Values(true, false));
|
||||||
|
|
||||||
TEST_P(QuicTransportImplTestUniBidi, AppLimitedTest) {
|
TEST_P(QuicTransportImplTestUniBidi, AppIdleTest) {
|
||||||
auto& conn = transport->getConnectionState();
|
auto& conn = transport->getConnectionState();
|
||||||
auto mockCongestionController = std::make_unique<MockCongestionController>();
|
auto mockCongestionController = std::make_unique<MockCongestionController>();
|
||||||
auto rawCongestionController = mockCongestionController.get();
|
auto rawCongestionController = mockCongestionController.get();
|
||||||
conn.congestionController = std::move(mockCongestionController);
|
conn.congestionController = std::move(mockCongestionController);
|
||||||
|
|
||||||
EXPECT_CALL(*rawCongestionController, setAppLimited(false, _)).Times(0);
|
EXPECT_CALL(*rawCongestionController, setAppIdle(false, _)).Times(0);
|
||||||
auto stream = createStream(transport, GetParam());
|
auto stream = createStream(transport, GetParam());
|
||||||
|
|
||||||
EXPECT_CALL(*rawCongestionController, setAppLimited(true, _));
|
EXPECT_CALL(*rawCongestionController, setAppIdle(true, _));
|
||||||
transport->closeStream(stream);
|
transport->closeStream(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(QuicTransportImplTestUniBidi, AppLimitedTestControlStreams) {
|
TEST_P(QuicTransportImplTestUniBidi, AppIdleTestControlStreams) {
|
||||||
auto& conn = transport->getConnectionState();
|
auto& conn = transport->getConnectionState();
|
||||||
auto mockCongestionController = std::make_unique<MockCongestionController>();
|
auto mockCongestionController = std::make_unique<MockCongestionController>();
|
||||||
auto rawCongestionController = mockCongestionController.get();
|
auto rawCongestionController = mockCongestionController.get();
|
||||||
conn.congestionController = std::move(mockCongestionController);
|
conn.congestionController = std::move(mockCongestionController);
|
||||||
|
|
||||||
EXPECT_CALL(*rawCongestionController, setAppLimited(false, _)).Times(0);
|
EXPECT_CALL(*rawCongestionController, setAppIdle(false, _)).Times(0);
|
||||||
auto stream = createStream(transport, GetParam());
|
auto stream = createStream(transport, GetParam());
|
||||||
ASSERT_TRUE(stream);
|
ASSERT_TRUE(stream);
|
||||||
|
|
||||||
@@ -1497,25 +1497,25 @@ TEST_P(QuicTransportImplTestUniBidi, AppLimitedTestControlStreams) {
|
|||||||
ASSERT_TRUE(ctrlStream2);
|
ASSERT_TRUE(ctrlStream2);
|
||||||
transport->setControlStream(ctrlStream2);
|
transport->setControlStream(ctrlStream2);
|
||||||
|
|
||||||
EXPECT_CALL(*rawCongestionController, setAppLimited(true, _));
|
EXPECT_CALL(*rawCongestionController, setAppIdle(true, _));
|
||||||
transport->closeStream(stream);
|
transport->closeStream(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(QuicTransportImplTestUniBidi, AppLimitedTestOnlyControlStreams) {
|
TEST_P(QuicTransportImplTestUniBidi, AppIdleTestOnlyControlStreams) {
|
||||||
auto& conn = transport->getConnectionState();
|
auto& conn = transport->getConnectionState();
|
||||||
auto mockCongestionController = std::make_unique<MockCongestionController>();
|
auto mockCongestionController = std::make_unique<MockCongestionController>();
|
||||||
auto rawCongestionController = mockCongestionController.get();
|
auto rawCongestionController = mockCongestionController.get();
|
||||||
conn.congestionController = std::move(mockCongestionController);
|
conn.congestionController = std::move(mockCongestionController);
|
||||||
|
|
||||||
auto ctrlStream1 = createStream(transport, GetParam());
|
auto ctrlStream1 = createStream(transport, GetParam());
|
||||||
EXPECT_CALL(*rawCongestionController, setAppLimited(true, _)).Times(1);
|
EXPECT_CALL(*rawCongestionController, setAppIdle(true, _)).Times(1);
|
||||||
transport->setControlStream(ctrlStream1);
|
transport->setControlStream(ctrlStream1);
|
||||||
EXPECT_CALL(*rawCongestionController, setAppLimited(false, _)).Times(1);
|
EXPECT_CALL(*rawCongestionController, setAppIdle(false, _)).Times(1);
|
||||||
auto ctrlStream2 = createStream(transport, GetParam());
|
auto ctrlStream2 = createStream(transport, GetParam());
|
||||||
EXPECT_CALL(*rawCongestionController, setAppLimited(true, _)).Times(1);
|
EXPECT_CALL(*rawCongestionController, setAppIdle(true, _)).Times(1);
|
||||||
transport->setControlStream(ctrlStream2);
|
transport->setControlStream(ctrlStream2);
|
||||||
|
|
||||||
EXPECT_CALL(*rawCongestionController, setAppLimited(_, _)).Times(0);
|
EXPECT_CALL(*rawCongestionController, setAppIdle(_, _)).Times(0);
|
||||||
transport->closeStream(ctrlStream1);
|
transport->closeStream(ctrlStream1);
|
||||||
transport->closeStream(ctrlStream2);
|
transport->closeStream(ctrlStream2);
|
||||||
}
|
}
|
||||||
|
@@ -358,8 +358,96 @@ TEST_F(QuicTransportTest, WriteDataWithProbing) {
|
|||||||
loopForWrites();
|
loopForWrites();
|
||||||
// Pending numProbePackets is cleared:
|
// Pending numProbePackets is cleared:
|
||||||
EXPECT_EQ(0, conn.pendingEvents.numProbePackets);
|
EXPECT_EQ(0, conn.pendingEvents.numProbePackets);
|
||||||
// both write and onPacketSent will inquire getWritableBytes
|
transport_->close(folly::none);
|
||||||
EXPECT_EQ(onPacketSentCounter + socketWriteCounter, getWritableBytesCounter);
|
}
|
||||||
|
|
||||||
|
TEST_F(QuicTransportTest, NotAppLimitedWithLoss) {
|
||||||
|
auto& conn = transport_->getConnectionState();
|
||||||
|
// Replace with MockConnectionCallback:
|
||||||
|
auto mockCongestionController = std::make_unique<MockCongestionController>();
|
||||||
|
auto rawCongestionController = mockCongestionController.get();
|
||||||
|
conn.congestionController = std::move(mockCongestionController);
|
||||||
|
EXPECT_CALL(*rawCongestionController, getWritableBytes())
|
||||||
|
.WillRepeatedly(Return(5000));
|
||||||
|
|
||||||
|
auto stream = transport_->createBidirectionalStream().value();
|
||||||
|
auto lossStream = transport_->createBidirectionalStream().value();
|
||||||
|
conn.streamManager->addLoss(lossStream);
|
||||||
|
conn.streamManager->getStream(lossStream)
|
||||||
|
->lossBuffer.emplace_back(
|
||||||
|
IOBuf::copyBuffer("Mountains may depart"), 0, false);
|
||||||
|
transport_->writeChain(
|
||||||
|
stream,
|
||||||
|
IOBuf::copyBuffer("An elephant sitting still"),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
nullptr);
|
||||||
|
EXPECT_CALL(*rawCongestionController, setAppLimited()).Times(0);
|
||||||
|
loopForWrites();
|
||||||
|
transport_->close(folly::none);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(QuicTransportTest, NotAppLimitedWithNoWritableBytes) {
|
||||||
|
auto& conn = transport_->getConnectionState();
|
||||||
|
// Replace with MockConnectionCallback:
|
||||||
|
auto mockCongestionController = std::make_unique<MockCongestionController>();
|
||||||
|
auto rawCongestionController = mockCongestionController.get();
|
||||||
|
conn.congestionController = std::move(mockCongestionController);
|
||||||
|
EXPECT_CALL(*rawCongestionController, getWritableBytes())
|
||||||
|
.WillRepeatedly(Invoke([&]() {
|
||||||
|
if (conn.outstandingPackets.empty()) {
|
||||||
|
return 5000;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}));
|
||||||
|
|
||||||
|
auto stream = transport_->createBidirectionalStream().value();
|
||||||
|
transport_->writeChain(
|
||||||
|
stream,
|
||||||
|
IOBuf::copyBuffer("An elephant sitting still"),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
nullptr);
|
||||||
|
EXPECT_CALL(*rawCongestionController, setAppLimited()).Times(0);
|
||||||
|
loopForWrites();
|
||||||
|
transport_->close(folly::none);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(QuicTransportTest, NotAppLimitedWithLargeBuffer) {
|
||||||
|
auto& conn = transport_->getConnectionState();
|
||||||
|
// Replace with MockConnectionCallback:
|
||||||
|
auto mockCongestionController = std::make_unique<MockCongestionController>();
|
||||||
|
auto rawCongestionController = mockCongestionController.get();
|
||||||
|
conn.congestionController = std::move(mockCongestionController);
|
||||||
|
EXPECT_CALL(*rawCongestionController, getWritableBytes())
|
||||||
|
.WillRepeatedly(Return(5000));
|
||||||
|
|
||||||
|
auto stream = transport_->createBidirectionalStream().value();
|
||||||
|
auto buf = buildRandomInputData(100 * 2000);
|
||||||
|
transport_->writeChain(stream, buf->clone(), false, false, nullptr);
|
||||||
|
EXPECT_CALL(*rawCongestionController, setAppLimited()).Times(0);
|
||||||
|
loopForWrites();
|
||||||
|
transport_->close(folly::none);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(QuicTransportTest, AppLimited) {
|
||||||
|
auto& conn = transport_->getConnectionState();
|
||||||
|
// Replace with MockConnectionCallback:
|
||||||
|
auto mockCongestionController = std::make_unique<MockCongestionController>();
|
||||||
|
auto rawCongestionController = mockCongestionController.get();
|
||||||
|
conn.congestionController = std::move(mockCongestionController);
|
||||||
|
EXPECT_CALL(*rawCongestionController, getWritableBytes())
|
||||||
|
.WillRepeatedly(Return(5000));
|
||||||
|
|
||||||
|
auto stream = transport_->createBidirectionalStream().value();
|
||||||
|
transport_->writeChain(
|
||||||
|
stream,
|
||||||
|
IOBuf::copyBuffer("An elephant sitting still"),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
nullptr);
|
||||||
|
EXPECT_CALL(*rawCongestionController, setAppLimited()).Times(1);
|
||||||
|
loopForWrites();
|
||||||
transport_->close(folly::none);
|
transport_->close(folly::none);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -308,7 +308,10 @@ std::chrono::microseconds Copa::getPacingInterval() const noexcept {
|
|||||||
|
|
||||||
void Copa::setMinimalPacingInterval(std::chrono::microseconds) noexcept {}
|
void Copa::setMinimalPacingInterval(std::chrono::microseconds) noexcept {}
|
||||||
|
|
||||||
void Copa::setAppLimited(bool, TimePoint) noexcept { /* unsupported */
|
void Copa::setAppIdle(bool, TimePoint) noexcept { /* unsupported */
|
||||||
|
}
|
||||||
|
|
||||||
|
void Copa::setAppLimited() { /* unsupported */
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Copa::isAppLimited() const noexcept {
|
bool Copa::isAppLimited() const noexcept {
|
||||||
|
@@ -43,7 +43,8 @@ class Copa : public CongestionController {
|
|||||||
uint64_t getBytesInFlight() const noexcept;
|
uint64_t getBytesInFlight() const noexcept;
|
||||||
|
|
||||||
void setConnectionEmulation(uint8_t) noexcept override;
|
void setConnectionEmulation(uint8_t) noexcept override;
|
||||||
void setAppLimited(bool, TimePoint) noexcept override;
|
void setAppIdle(bool, TimePoint) noexcept override;
|
||||||
|
void setAppLimited() override;
|
||||||
|
|
||||||
// pacing related functions, not implemented for copa
|
// pacing related functions, not implemented for copa
|
||||||
bool canBePaced() const noexcept override;
|
bool canBePaced() const noexcept override;
|
||||||
|
@@ -158,7 +158,10 @@ std::chrono::microseconds NewReno::getPacingInterval() const noexcept {
|
|||||||
|
|
||||||
void NewReno::setMinimalPacingInterval(std::chrono::microseconds) noexcept {}
|
void NewReno::setMinimalPacingInterval(std::chrono::microseconds) noexcept {}
|
||||||
|
|
||||||
void NewReno::setAppLimited(bool, TimePoint) noexcept { /* unsupported */
|
void NewReno::setAppIdle(bool, TimePoint) noexcept { /* unsupported */
|
||||||
|
}
|
||||||
|
|
||||||
|
void NewReno::setAppLimited() { /* unsupported */
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NewReno::isAppLimited() const noexcept {
|
bool NewReno::isAppLimited() const noexcept {
|
||||||
|
@@ -27,7 +27,9 @@ class NewReno : public CongestionController {
|
|||||||
uint64_t getCongestionWindow() const noexcept override;
|
uint64_t getCongestionWindow() const noexcept override;
|
||||||
void setConnectionEmulation(uint8_t) noexcept override;
|
void setConnectionEmulation(uint8_t) noexcept override;
|
||||||
bool canBePaced() const noexcept override;
|
bool canBePaced() const noexcept override;
|
||||||
void setAppLimited(bool, TimePoint) noexcept override;
|
void setAppIdle(bool, TimePoint) noexcept override;
|
||||||
|
void setAppLimited() override;
|
||||||
|
|
||||||
CongestionControlType type() const noexcept override;
|
CongestionControlType type() const noexcept override;
|
||||||
|
|
||||||
bool inSlowStart() const noexcept;
|
bool inSlowStart() const noexcept;
|
||||||
|
@@ -154,11 +154,11 @@ bool Cubic::canBePaced() const noexcept {
|
|||||||
return pacingEnabled;
|
return pacingEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cubic::setAppLimited(bool limited, TimePoint eventTime) noexcept {
|
void Cubic::setAppIdle(bool idle, TimePoint eventTime) noexcept {
|
||||||
QUIC_TRACE(
|
QUIC_TRACE(
|
||||||
cubic_applimited,
|
cubic_appidle,
|
||||||
conn_,
|
conn_,
|
||||||
limited,
|
idle,
|
||||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
eventTime.time_since_epoch())
|
eventTime.time_since_epoch())
|
||||||
.count(),
|
.count(),
|
||||||
@@ -167,22 +167,32 @@ void Cubic::setAppLimited(bool limited, TimePoint eventTime) noexcept {
|
|||||||
steadyState_.lastReductionTime->time_since_epoch())
|
steadyState_.lastReductionTime->time_since_epoch())
|
||||||
.count()
|
.count()
|
||||||
: -1);
|
: -1);
|
||||||
bool currentAppLimited = isAppLimited();
|
bool currentAppIdle = isAppIdle();
|
||||||
if (!currentAppLimited && limited) {
|
if (!currentAppIdle && idle) {
|
||||||
quiescenceStart_ = eventTime;
|
quiescenceStart_ = eventTime;
|
||||||
}
|
}
|
||||||
if (!limited && currentAppLimited && *quiescenceStart_ <= eventTime &&
|
if (!idle && currentAppIdle && *quiescenceStart_ <= eventTime &&
|
||||||
steadyState_.lastReductionTime) {
|
steadyState_.lastReductionTime) {
|
||||||
*steadyState_.lastReductionTime +=
|
*steadyState_.lastReductionTime +=
|
||||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
eventTime - *quiescenceStart_);
|
eventTime - *quiescenceStart_);
|
||||||
}
|
}
|
||||||
if (!limited) {
|
if (!idle) {
|
||||||
quiescenceStart_ = folly::none;
|
quiescenceStart_ = folly::none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Cubic::setAppLimited() {
|
||||||
|
// we use app-idle for Cubic
|
||||||
|
}
|
||||||
|
|
||||||
bool Cubic::isAppLimited() const noexcept {
|
bool Cubic::isAppLimited() const noexcept {
|
||||||
|
// Or maybe always false. This doesn't really matter for Cubic. Channeling
|
||||||
|
// isAppIdle() makes testing easier.
|
||||||
|
return isAppIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Cubic::isAppIdle() const noexcept {
|
||||||
return quiescenceStart_.hasValue();
|
return quiescenceStart_.hasValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -97,7 +97,8 @@ class Cubic : public CongestionController {
|
|||||||
uint64_t getCongestionWindow() const noexcept override;
|
uint64_t getCongestionWindow() const noexcept override;
|
||||||
bool canBePaced() const noexcept override;
|
bool canBePaced() const noexcept override;
|
||||||
void setConnectionEmulation(uint8_t) noexcept override;
|
void setConnectionEmulation(uint8_t) noexcept override;
|
||||||
void setAppLimited(bool limited, TimePoint eventTime) noexcept override;
|
void setAppIdle(bool idle, TimePoint eventTime) noexcept override;
|
||||||
|
void setAppLimited() override;
|
||||||
|
|
||||||
bool isAppLimited() const noexcept override;
|
bool isAppLimited() const noexcept override;
|
||||||
|
|
||||||
@@ -116,6 +117,7 @@ class Cubic : public CongestionController {
|
|||||||
CubicStates state_{CubicStates::Hystart};
|
CubicStates state_{CubicStates::Hystart};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool isAppIdle() const noexcept;
|
||||||
void onPacketAcked(const AckEvent& ack);
|
void onPacketAcked(const AckEvent& ack);
|
||||||
void onPacketAckedInHystart(const AckEvent& ack);
|
void onPacketAckedInHystart(const AckEvent& ack);
|
||||||
void onPacketAckedInSteady(const AckEvent& ack);
|
void onPacketAckedInSteady(const AckEvent& ack);
|
||||||
|
@@ -124,7 +124,7 @@ TEST_F(CubicTest, CwndIncreaseAfterReduction) {
|
|||||||
EXPECT_EQ(CubicStates::Steady, cubic.state());
|
EXPECT_EQ(CubicStates::Steady, cubic.state());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CubicTest, AppLimited) {
|
TEST_F(CubicTest, AppIdle) {
|
||||||
QuicConnectionStateBase conn(QuicNodeType::Client);
|
QuicConnectionStateBase conn(QuicNodeType::Client);
|
||||||
conn.udpSendPacketLen = 1500;
|
conn.udpSendPacketLen = 1500;
|
||||||
TestingCubic cubic(conn);
|
TestingCubic cubic(conn);
|
||||||
@@ -150,7 +150,7 @@ TEST_F(CubicTest, AppLimited) {
|
|||||||
EXPECT_GT(cubic.getCongestionWindow(), cwnd);
|
EXPECT_GT(cubic.getCongestionWindow(), cwnd);
|
||||||
cwnd = cubic.getCongestionWindow();
|
cwnd = cubic.getCongestionWindow();
|
||||||
|
|
||||||
cubic.setAppLimited(true, reductionTime + 1100ms);
|
cubic.setAppIdle(true, reductionTime + 1100ms);
|
||||||
EXPECT_TRUE(cubic.isAppLimited());
|
EXPECT_TRUE(cubic.isAppLimited());
|
||||||
auto packet2 = makeTestingWritePacket(2, 1000, 3000);
|
auto packet2 = makeTestingWritePacket(2, 1000, 3000);
|
||||||
cubic.onPacketSent(packet2);
|
cubic.onPacketSent(packet2);
|
||||||
@@ -159,7 +159,7 @@ TEST_F(CubicTest, AppLimited) {
|
|||||||
EXPECT_EQ(cubic.getCongestionWindow(), cwnd);
|
EXPECT_EQ(cubic.getCongestionWindow(), cwnd);
|
||||||
|
|
||||||
// 1 seconds of quiescence
|
// 1 seconds of quiescence
|
||||||
cubic.setAppLimited(false, reductionTime + 2100ms);
|
cubic.setAppIdle(false, reductionTime + 2100ms);
|
||||||
EXPECT_FALSE(cubic.isAppLimited());
|
EXPECT_FALSE(cubic.isAppLimited());
|
||||||
auto packet3 = makeTestingWritePacket(3, 1000, 4000);
|
auto packet3 = makeTestingWritePacket(3, 1000, 4000);
|
||||||
cubic.onPacketSent(packet3);
|
cubic.onPacketSent(packet3);
|
||||||
|
@@ -153,7 +153,7 @@ QuicStreamManager::getOrCreateOpenedLocalStream(StreamId streamId) {
|
|||||||
QuicStreamState* QuicStreamManager::getStream(StreamId streamId) {
|
QuicStreamState* QuicStreamManager::getStream(StreamId streamId) {
|
||||||
if (isRemoteStream(nodeType_, streamId)) {
|
if (isRemoteStream(nodeType_, streamId)) {
|
||||||
auto stream = getOrCreatePeerStream(streamId);
|
auto stream = getOrCreatePeerStream(streamId);
|
||||||
updateAppLimitedState();
|
updateAppIdleState();
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
auto it = streams_.find(streamId);
|
auto it = streams_.find(streamId);
|
||||||
@@ -169,7 +169,7 @@ QuicStreamState* QuicStreamManager::getStream(StreamId streamId) {
|
|||||||
"Trying to get unopened local stream",
|
"Trying to get unopened local stream",
|
||||||
TransportErrorCode::STREAM_STATE_ERROR);
|
TransportErrorCode::STREAM_STATE_ERROR);
|
||||||
}
|
}
|
||||||
updateAppLimitedState();
|
updateAppIdleState();
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +298,7 @@ QuicStreamManager::createStream(StreamId streamId) {
|
|||||||
std::forward_as_tuple(streamId),
|
std::forward_as_tuple(streamId),
|
||||||
std::forward_as_tuple(streamId, conn_));
|
std::forward_as_tuple(streamId, conn_));
|
||||||
QUIC_STATS(conn_.infoCallback, onNewQuicStream);
|
QUIC_STATS(conn_.infoCallback, onNewQuicStream);
|
||||||
updateAppLimitedState();
|
updateAppIdleState();
|
||||||
return &it.first->second;
|
return &it.first->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,7 +342,7 @@ void QuicStreamManager::removeClosedStream(StreamId streamId) {
|
|||||||
openLocalStreams_.erase(streamItr);
|
openLocalStreams_.erase(streamItr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateAppLimitedState();
|
updateAppIdleState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuicStreamManager::updateLossStreams(QuicStreamState& stream) {
|
void QuicStreamManager::updateLossStreams(QuicStreamState& stream) {
|
||||||
@@ -403,18 +403,18 @@ void QuicStreamManager::updatePeekableStreams(QuicStreamState& stream) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuicStreamManager::updateAppLimitedState() {
|
void QuicStreamManager::updateAppIdleState() {
|
||||||
bool currentNonCtrlStreams = hasNonCtrlStreams();
|
bool currentNonCtrlStreams = hasNonCtrlStreams();
|
||||||
if (isAppLimited_ && !currentNonCtrlStreams) {
|
if (isAppIdle_ && !currentNonCtrlStreams) {
|
||||||
// We were app limited, and we continue to be app limited.
|
// We were app limited, and we continue to be app limited.
|
||||||
return;
|
return;
|
||||||
} else if (!isAppLimited_ && currentNonCtrlStreams) {
|
} else if (!isAppIdle_ && currentNonCtrlStreams) {
|
||||||
// We were not app limited, and we continue to be not app limited.
|
// We were not app limited, and we continue to be not app limited.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
isAppLimited_ = !currentNonCtrlStreams;
|
isAppIdle_ = !currentNonCtrlStreams;
|
||||||
if (conn_.congestionController) {
|
if (conn_.congestionController) {
|
||||||
conn_.congestionController->setAppLimited(isAppLimited_, Clock::now());
|
conn_.congestionController->setAppIdle(isAppIdle_, Clock::now());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,10 +423,10 @@ void QuicStreamManager::setStreamAsControl(QuicStreamState& stream) {
|
|||||||
stream.isControl = true;
|
stream.isControl = true;
|
||||||
numControlStreams_++;
|
numControlStreams_++;
|
||||||
}
|
}
|
||||||
updateAppLimitedState();
|
updateAppIdleState();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QuicStreamManager::isAppLimited() const {
|
bool QuicStreamManager::isAppIdle() const {
|
||||||
return isAppLimited_;
|
return isAppIdle_;
|
||||||
}
|
}
|
||||||
} // namespace quic
|
} // namespace quic
|
||||||
|
@@ -563,16 +563,16 @@ class QuicStreamManager {
|
|||||||
dataRejectedStreams_.clear();
|
dataRejectedStreams_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isAppLimited() const;
|
bool isAppIdle() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Updates the congestion controller app limited state, after a change in the
|
// Updates the congestion controller app-idle state, after a change in the
|
||||||
// number of streams.
|
// number of streams.
|
||||||
// App limited state is set to true if there was at least one non-control
|
// App-idle state is set to true if there was at least one non-control
|
||||||
// before the update and there are none after. It is set to false if instead
|
// before the update and there are none after. It is set to false if instead
|
||||||
// there were no non-control streams before and there is at least one at the
|
// there were no non-control streams before and there is at least one at the
|
||||||
// time of calling
|
// time of calling
|
||||||
void updateAppLimitedState();
|
void updateAppIdleState();
|
||||||
|
|
||||||
QuicStreamState* FOLLY_NULLABLE
|
QuicStreamState* FOLLY_NULLABLE
|
||||||
getOrCreateOpenedLocalStream(StreamId streamId);
|
getOrCreateOpenedLocalStream(StreamId streamId);
|
||||||
@@ -666,8 +666,8 @@ class QuicStreamManager {
|
|||||||
// Data structure to keep track of stream that have detected lost data
|
// Data structure to keep track of stream that have detected lost data
|
||||||
std::vector<StreamId> lossStreams_;
|
std::vector<StreamId> lossStreams_;
|
||||||
|
|
||||||
// Record whether or not we are app limited.
|
// Record whether or not we are app-idle.
|
||||||
bool isAppLimited_{false};
|
bool isAppIdle_{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace quic
|
} // namespace quic
|
||||||
|
@@ -218,15 +218,21 @@ struct CongestionController {
|
|||||||
* example, we probably don't want to pace a connection in Recovery.
|
* example, we probably don't want to pace a connection in Recovery.
|
||||||
*/
|
*/
|
||||||
virtual bool canBePaced() const = 0;
|
virtual bool canBePaced() const = 0;
|
||||||
|
/**
|
||||||
|
* Notify congestion controller that the connection has become idle or active
|
||||||
|
* in the sense that there are active non-control streams.
|
||||||
|
* idle: true if the connection has become app-idle, false if the
|
||||||
|
* connection has become not app-idle.
|
||||||
|
* eventTime: the time point when the app-idle state changed.
|
||||||
|
*/
|
||||||
|
virtual void setAppIdle(bool idle, TimePoint eventTime) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify congestion controller that the connection has become app-limited or
|
* Notify congestion controller that the connection has become app-limited or
|
||||||
* not app-limited.
|
* not app-limited.
|
||||||
*
|
*
|
||||||
* limited: true if the connection has become app-limited, false if the
|
|
||||||
* connection has become not app-limited.
|
|
||||||
* eventTime: the time point when the app-limited state changed.
|
|
||||||
*/
|
*/
|
||||||
virtual void setAppLimited(bool limited, TimePoint eventTime) = 0;
|
virtual void setAppLimited() = 0;
|
||||||
virtual CongestionControlType type() const = 0;
|
virtual CongestionControlType type() const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -246,8 +252,7 @@ struct CongestionController {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the congestion controller thinks it's currently in app-limited
|
* Whether the congestion controller thinks it's currently in app-limited
|
||||||
* state. For some congestion controllers, e.g. BBR, the app-limited state may
|
* state.
|
||||||
* not be the same as the app.
|
|
||||||
*/
|
*/
|
||||||
virtual bool isAppLimited() const = 0;
|
virtual bool isAppLimited() const = 0;
|
||||||
};
|
};
|
||||||
|
@@ -37,7 +37,8 @@ class MockCongestionController : public CongestionController {
|
|||||||
,
|
,
|
||||||
setMinimalPacingInterval,
|
setMinimalPacingInterval,
|
||||||
void(std::chrono::microseconds));
|
void(std::chrono::microseconds));
|
||||||
GMOCK_METHOD2_(, , , setAppLimited, void(bool, TimePoint));
|
GMOCK_METHOD2_(, , , setAppIdle, void(bool, TimePoint));
|
||||||
|
MOCK_METHOD0(setAppLimited, void());
|
||||||
MOCK_CONST_METHOD0(isAppLimited, bool());
|
MOCK_CONST_METHOD0(isAppLimited, bool());
|
||||||
};
|
};
|
||||||
} // namespace test
|
} // namespace test
|
||||||
|
@@ -44,140 +44,140 @@ class QuicStreamManagerTest : public Test {
|
|||||||
MockCongestionController* mockController;
|
MockCongestionController* mockController;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(QuicStreamManagerTest, TestAppLimitedCreateBidiStream) {
|
TEST_F(QuicStreamManagerTest, TestAppIdleCreateBidiStream) {
|
||||||
auto& manager = *conn.streamManager;
|
auto& manager = *conn.streamManager;
|
||||||
EXPECT_FALSE(manager.isAppLimited());
|
EXPECT_FALSE(manager.isAppIdle());
|
||||||
|
|
||||||
// The app limiited state did not change.
|
// The app limiited state did not change.
|
||||||
EXPECT_CALL(*mockController, setAppLimited(false, _)).Times(0);
|
EXPECT_CALL(*mockController, setAppIdle(false, _)).Times(0);
|
||||||
auto stream = manager.createNextBidirectionalStream();
|
auto stream = manager.createNextBidirectionalStream();
|
||||||
StreamId id = stream.value()->id;
|
StreamId id = stream.value()->id;
|
||||||
EXPECT_FALSE(manager.isAppLimited());
|
EXPECT_FALSE(manager.isAppIdle());
|
||||||
|
|
||||||
EXPECT_CALL(*mockController, setAppLimited(true, _));
|
EXPECT_CALL(*mockController, setAppIdle(true, _));
|
||||||
// Force transition to closed state
|
// Force transition to closed state
|
||||||
stream.value()->send.state = StreamSendStates::Closed();
|
stream.value()->send.state = StreamSendStates::Closed();
|
||||||
stream.value()->recv.state = StreamReceiveStates::Closed();
|
stream.value()->recv.state = StreamReceiveStates::Closed();
|
||||||
manager.removeClosedStream(stream.value()->id);
|
manager.removeClosedStream(stream.value()->id);
|
||||||
EXPECT_TRUE(manager.isAppLimited());
|
EXPECT_TRUE(manager.isAppIdle());
|
||||||
EXPECT_EQ(manager.getStream(id), nullptr);
|
EXPECT_EQ(manager.getStream(id), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(QuicStreamManagerTest, TestAppLimitedCreateUnidiStream) {
|
TEST_F(QuicStreamManagerTest, TestAppIdleCreateUnidiStream) {
|
||||||
auto& manager = *conn.streamManager;
|
auto& manager = *conn.streamManager;
|
||||||
EXPECT_FALSE(manager.isAppLimited());
|
EXPECT_FALSE(manager.isAppIdle());
|
||||||
EXPECT_CALL(*mockController, setAppLimited(false, _)).Times(0);
|
EXPECT_CALL(*mockController, setAppIdle(false, _)).Times(0);
|
||||||
auto stream = manager.createNextUnidirectionalStream();
|
auto stream = manager.createNextUnidirectionalStream();
|
||||||
EXPECT_FALSE(manager.isAppLimited());
|
EXPECT_FALSE(manager.isAppIdle());
|
||||||
|
|
||||||
// Force transition to closed state
|
// Force transition to closed state
|
||||||
EXPECT_CALL(*mockController, setAppLimited(true, _));
|
EXPECT_CALL(*mockController, setAppIdle(true, _));
|
||||||
stream.value()->send.state = StreamSendStates::Closed();
|
stream.value()->send.state = StreamSendStates::Closed();
|
||||||
stream.value()->recv.state = StreamReceiveStates::Closed();
|
stream.value()->recv.state = StreamReceiveStates::Closed();
|
||||||
manager.removeClosedStream(stream.value()->id);
|
manager.removeClosedStream(stream.value()->id);
|
||||||
EXPECT_TRUE(manager.isAppLimited());
|
EXPECT_TRUE(manager.isAppIdle());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(QuicStreamManagerTest, TestAppLimitedExistingLocalStream) {
|
TEST_F(QuicStreamManagerTest, TestAppIdleExistingLocalStream) {
|
||||||
auto& manager = *conn.streamManager;
|
auto& manager = *conn.streamManager;
|
||||||
EXPECT_FALSE(manager.isAppLimited());
|
EXPECT_FALSE(manager.isAppIdle());
|
||||||
EXPECT_CALL(*mockController, setAppLimited(false, _)).Times(0);
|
EXPECT_CALL(*mockController, setAppIdle(false, _)).Times(0);
|
||||||
|
|
||||||
auto stream = manager.createNextUnidirectionalStream();
|
auto stream = manager.createNextUnidirectionalStream();
|
||||||
EXPECT_FALSE(manager.isAppLimited());
|
EXPECT_FALSE(manager.isAppIdle());
|
||||||
|
|
||||||
EXPECT_CALL(*mockController, setAppLimited(true, _));
|
EXPECT_CALL(*mockController, setAppIdle(true, _));
|
||||||
manager.setStreamAsControl(*stream.value());
|
manager.setStreamAsControl(*stream.value());
|
||||||
EXPECT_TRUE(manager.isAppLimited());
|
EXPECT_TRUE(manager.isAppIdle());
|
||||||
|
|
||||||
manager.getStream(stream.value()->id);
|
manager.getStream(stream.value()->id);
|
||||||
EXPECT_TRUE(manager.isAppLimited());
|
EXPECT_TRUE(manager.isAppIdle());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(QuicStreamManagerTest, TestAppLimitedStreamAsControl) {
|
TEST_F(QuicStreamManagerTest, TestAppIdleStreamAsControl) {
|
||||||
auto& manager = *conn.streamManager;
|
auto& manager = *conn.streamManager;
|
||||||
EXPECT_FALSE(manager.isAppLimited());
|
EXPECT_FALSE(manager.isAppIdle());
|
||||||
|
|
||||||
auto stream = manager.createNextUnidirectionalStream();
|
auto stream = manager.createNextUnidirectionalStream();
|
||||||
EXPECT_FALSE(manager.isAppLimited());
|
EXPECT_FALSE(manager.isAppIdle());
|
||||||
|
|
||||||
EXPECT_CALL(*mockController, setAppLimited(true, _));
|
EXPECT_CALL(*mockController, setAppIdle(true, _));
|
||||||
manager.setStreamAsControl(*stream.value());
|
manager.setStreamAsControl(*stream.value());
|
||||||
EXPECT_TRUE(manager.isAppLimited());
|
EXPECT_TRUE(manager.isAppIdle());
|
||||||
|
|
||||||
EXPECT_CALL(*mockController, setAppLimited(false, _));
|
EXPECT_CALL(*mockController, setAppIdle(false, _));
|
||||||
manager.createNextUnidirectionalStream();
|
manager.createNextUnidirectionalStream();
|
||||||
EXPECT_FALSE(manager.isAppLimited());
|
EXPECT_FALSE(manager.isAppIdle());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(QuicStreamManagerTest, TestAppLimitedCreatePeerStream) {
|
TEST_F(QuicStreamManagerTest, TestAppIdleCreatePeerStream) {
|
||||||
auto& manager = *conn.streamManager;
|
auto& manager = *conn.streamManager;
|
||||||
EXPECT_FALSE(manager.isAppLimited());
|
EXPECT_FALSE(manager.isAppIdle());
|
||||||
StreamId id = 0;
|
StreamId id = 0;
|
||||||
auto stream = manager.getStream(id);
|
auto stream = manager.getStream(id);
|
||||||
EXPECT_FALSE(manager.isAppLimited());
|
EXPECT_FALSE(manager.isAppIdle());
|
||||||
|
|
||||||
EXPECT_CALL(*mockController, setAppLimited(true, _));
|
EXPECT_CALL(*mockController, setAppIdle(true, _));
|
||||||
manager.setStreamAsControl(*stream);
|
manager.setStreamAsControl(*stream);
|
||||||
EXPECT_TRUE(manager.isAppLimited());
|
EXPECT_TRUE(manager.isAppIdle());
|
||||||
|
|
||||||
EXPECT_CALL(*mockController, setAppLimited(false, _));
|
EXPECT_CALL(*mockController, setAppIdle(false, _));
|
||||||
StreamId id2 = 4;
|
StreamId id2 = 4;
|
||||||
manager.getStream(id2);
|
manager.getStream(id2);
|
||||||
EXPECT_FALSE(manager.isAppLimited());
|
EXPECT_FALSE(manager.isAppIdle());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(QuicStreamManagerTest, TestAppLimitedExistingPeerStream) {
|
TEST_F(QuicStreamManagerTest, TestAppIdleExistingPeerStream) {
|
||||||
auto& manager = *conn.streamManager;
|
auto& manager = *conn.streamManager;
|
||||||
EXPECT_FALSE(manager.isAppLimited());
|
EXPECT_FALSE(manager.isAppIdle());
|
||||||
EXPECT_CALL(*mockController, setAppLimited(false, _)).Times(0);
|
EXPECT_CALL(*mockController, setAppIdle(false, _)).Times(0);
|
||||||
|
|
||||||
StreamId id = 0;
|
StreamId id = 0;
|
||||||
auto stream = manager.getStream(id);
|
auto stream = manager.getStream(id);
|
||||||
EXPECT_FALSE(manager.isAppLimited());
|
EXPECT_FALSE(manager.isAppIdle());
|
||||||
|
|
||||||
EXPECT_CALL(*mockController, setAppLimited(true, _));
|
EXPECT_CALL(*mockController, setAppIdle(true, _));
|
||||||
manager.setStreamAsControl(*stream);
|
manager.setStreamAsControl(*stream);
|
||||||
EXPECT_TRUE(manager.isAppLimited());
|
EXPECT_TRUE(manager.isAppIdle());
|
||||||
|
|
||||||
manager.getStream(id);
|
manager.getStream(id);
|
||||||
EXPECT_TRUE(manager.isAppLimited());
|
EXPECT_TRUE(manager.isAppIdle());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(QuicStreamManagerTest, TestAppLimitedClosePeerStream) {
|
TEST_F(QuicStreamManagerTest, TestAppIdleClosePeerStream) {
|
||||||
auto& manager = *conn.streamManager;
|
auto& manager = *conn.streamManager;
|
||||||
EXPECT_FALSE(manager.isAppLimited());
|
EXPECT_FALSE(manager.isAppIdle());
|
||||||
StreamId id = 0;
|
StreamId id = 0;
|
||||||
auto stream = manager.getStream(id);
|
auto stream = manager.getStream(id);
|
||||||
EXPECT_FALSE(manager.isAppLimited());
|
EXPECT_FALSE(manager.isAppIdle());
|
||||||
|
|
||||||
EXPECT_CALL(*mockController, setAppLimited(true, _));
|
EXPECT_CALL(*mockController, setAppIdle(true, _));
|
||||||
// Force transition to closed state
|
// Force transition to closed state
|
||||||
stream->send.state = StreamSendStates::Closed();
|
stream->send.state = StreamSendStates::Closed();
|
||||||
stream->recv.state = StreamReceiveStates::Closed();
|
stream->recv.state = StreamReceiveStates::Closed();
|
||||||
manager.removeClosedStream(stream->id);
|
manager.removeClosedStream(stream->id);
|
||||||
EXPECT_TRUE(manager.isAppLimited());
|
EXPECT_TRUE(manager.isAppIdle());
|
||||||
EXPECT_EQ(manager.getStream(id), nullptr);
|
EXPECT_EQ(manager.getStream(id), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(QuicStreamManagerTest, TestAppLimitedCloseControlStream) {
|
TEST_F(QuicStreamManagerTest, TestAppIdleCloseControlStream) {
|
||||||
auto& manager = *conn.streamManager;
|
auto& manager = *conn.streamManager;
|
||||||
EXPECT_FALSE(manager.isAppLimited());
|
EXPECT_FALSE(manager.isAppIdle());
|
||||||
EXPECT_CALL(*mockController, setAppLimited(false, _)).Times(0);
|
EXPECT_CALL(*mockController, setAppIdle(false, _)).Times(0);
|
||||||
|
|
||||||
StreamId id = 0;
|
StreamId id = 0;
|
||||||
auto stream = manager.getStream(id);
|
auto stream = manager.getStream(id);
|
||||||
EXPECT_FALSE(manager.isAppLimited());
|
EXPECT_FALSE(manager.isAppIdle());
|
||||||
|
|
||||||
EXPECT_CALL(*mockController, setAppLimited(true, _));
|
EXPECT_CALL(*mockController, setAppIdle(true, _));
|
||||||
manager.setStreamAsControl(*stream);
|
manager.setStreamAsControl(*stream);
|
||||||
EXPECT_TRUE(manager.isAppLimited());
|
EXPECT_TRUE(manager.isAppIdle());
|
||||||
|
|
||||||
// Force transition to closed state
|
// Force transition to closed state
|
||||||
stream->send.state = StreamSendStates::Closed();
|
stream->send.state = StreamSendStates::Closed();
|
||||||
stream->recv.state = StreamReceiveStates::Closed();
|
stream->recv.state = StreamReceiveStates::Closed();
|
||||||
manager.removeClosedStream(stream->id);
|
manager.removeClosedStream(stream->id);
|
||||||
EXPECT_TRUE(manager.isAppLimited());
|
EXPECT_TRUE(manager.isAppIdle());
|
||||||
}
|
}
|
||||||
} // namespace test
|
} // namespace test
|
||||||
} // namespace quic
|
} // namespace quic
|
||||||
|
Reference in New Issue
Block a user