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();
|
||||
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
|
||||
|
@@ -366,7 +366,8 @@ void updateConnection(
|
||||
packetNum,
|
||||
(uint64_t)encodedSize,
|
||||
(int)isHandshake,
|
||||
(int)pureAck);
|
||||
(int)pureAck,
|
||||
pkt.isAppLimited);
|
||||
conn.lossState.largestSent = std::max(conn.lossState.largestSent, packetNum);
|
||||
if (conn.congestionController && !pureAck) {
|
||||
conn.congestionController->onPacketSent(pkt);
|
||||
|
@@ -1467,26 +1467,26 @@ INSTANTIATE_TEST_CASE_P(
|
||||
QuicTransportImplTestUniBidi,
|
||||
Values(true, false));
|
||||
|
||||
TEST_P(QuicTransportImplTestUniBidi, AppLimitedTest) {
|
||||
TEST_P(QuicTransportImplTestUniBidi, AppIdleTest) {
|
||||
auto& conn = transport->getConnectionState();
|
||||
auto mockCongestionController = std::make_unique<MockCongestionController>();
|
||||
auto rawCongestionController = mockCongestionController.get();
|
||||
conn.congestionController = std::move(mockCongestionController);
|
||||
|
||||
EXPECT_CALL(*rawCongestionController, setAppLimited(false, _)).Times(0);
|
||||
EXPECT_CALL(*rawCongestionController, setAppIdle(false, _)).Times(0);
|
||||
auto stream = createStream(transport, GetParam());
|
||||
|
||||
EXPECT_CALL(*rawCongestionController, setAppLimited(true, _));
|
||||
EXPECT_CALL(*rawCongestionController, setAppIdle(true, _));
|
||||
transport->closeStream(stream);
|
||||
}
|
||||
|
||||
TEST_P(QuicTransportImplTestUniBidi, AppLimitedTestControlStreams) {
|
||||
TEST_P(QuicTransportImplTestUniBidi, AppIdleTestControlStreams) {
|
||||
auto& conn = transport->getConnectionState();
|
||||
auto mockCongestionController = std::make_unique<MockCongestionController>();
|
||||
auto rawCongestionController = mockCongestionController.get();
|
||||
conn.congestionController = std::move(mockCongestionController);
|
||||
|
||||
EXPECT_CALL(*rawCongestionController, setAppLimited(false, _)).Times(0);
|
||||
EXPECT_CALL(*rawCongestionController, setAppIdle(false, _)).Times(0);
|
||||
auto stream = createStream(transport, GetParam());
|
||||
ASSERT_TRUE(stream);
|
||||
|
||||
@@ -1497,25 +1497,25 @@ TEST_P(QuicTransportImplTestUniBidi, AppLimitedTestControlStreams) {
|
||||
ASSERT_TRUE(ctrlStream2);
|
||||
transport->setControlStream(ctrlStream2);
|
||||
|
||||
EXPECT_CALL(*rawCongestionController, setAppLimited(true, _));
|
||||
EXPECT_CALL(*rawCongestionController, setAppIdle(true, _));
|
||||
transport->closeStream(stream);
|
||||
}
|
||||
|
||||
TEST_P(QuicTransportImplTestUniBidi, AppLimitedTestOnlyControlStreams) {
|
||||
TEST_P(QuicTransportImplTestUniBidi, AppIdleTestOnlyControlStreams) {
|
||||
auto& conn = transport->getConnectionState();
|
||||
auto mockCongestionController = std::make_unique<MockCongestionController>();
|
||||
auto rawCongestionController = mockCongestionController.get();
|
||||
conn.congestionController = std::move(mockCongestionController);
|
||||
|
||||
auto ctrlStream1 = createStream(transport, GetParam());
|
||||
EXPECT_CALL(*rawCongestionController, setAppLimited(true, _)).Times(1);
|
||||
EXPECT_CALL(*rawCongestionController, setAppIdle(true, _)).Times(1);
|
||||
transport->setControlStream(ctrlStream1);
|
||||
EXPECT_CALL(*rawCongestionController, setAppLimited(false, _)).Times(1);
|
||||
EXPECT_CALL(*rawCongestionController, setAppIdle(false, _)).Times(1);
|
||||
auto ctrlStream2 = createStream(transport, GetParam());
|
||||
EXPECT_CALL(*rawCongestionController, setAppLimited(true, _)).Times(1);
|
||||
EXPECT_CALL(*rawCongestionController, setAppIdle(true, _)).Times(1);
|
||||
transport->setControlStream(ctrlStream2);
|
||||
|
||||
EXPECT_CALL(*rawCongestionController, setAppLimited(_, _)).Times(0);
|
||||
EXPECT_CALL(*rawCongestionController, setAppIdle(_, _)).Times(0);
|
||||
transport->closeStream(ctrlStream1);
|
||||
transport->closeStream(ctrlStream2);
|
||||
}
|
||||
|
@@ -358,8 +358,96 @@ TEST_F(QuicTransportTest, WriteDataWithProbing) {
|
||||
loopForWrites();
|
||||
// Pending numProbePackets is cleared:
|
||||
EXPECT_EQ(0, conn.pendingEvents.numProbePackets);
|
||||
// both write and onPacketSent will inquire getWritableBytes
|
||||
EXPECT_EQ(onPacketSentCounter + socketWriteCounter, getWritableBytesCounter);
|
||||
transport_->close(folly::none);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@@ -308,7 +308,10 @@ std::chrono::microseconds Copa::getPacingInterval() const 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 {
|
||||
|
@@ -43,7 +43,8 @@ class Copa : public CongestionController {
|
||||
uint64_t getBytesInFlight() const noexcept;
|
||||
|
||||
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
|
||||
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::setAppLimited(bool, TimePoint) noexcept { /* unsupported */
|
||||
void NewReno::setAppIdle(bool, TimePoint) noexcept { /* unsupported */
|
||||
}
|
||||
|
||||
void NewReno::setAppLimited() { /* unsupported */
|
||||
}
|
||||
|
||||
bool NewReno::isAppLimited() const noexcept {
|
||||
|
@@ -27,7 +27,9 @@ class NewReno : public CongestionController {
|
||||
uint64_t getCongestionWindow() const noexcept override;
|
||||
void setConnectionEmulation(uint8_t) 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;
|
||||
|
||||
bool inSlowStart() const noexcept;
|
||||
|
@@ -154,11 +154,11 @@ bool Cubic::canBePaced() const noexcept {
|
||||
return pacingEnabled;
|
||||
}
|
||||
|
||||
void Cubic::setAppLimited(bool limited, TimePoint eventTime) noexcept {
|
||||
void Cubic::setAppIdle(bool idle, TimePoint eventTime) noexcept {
|
||||
QUIC_TRACE(
|
||||
cubic_applimited,
|
||||
cubic_appidle,
|
||||
conn_,
|
||||
limited,
|
||||
idle,
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
eventTime.time_since_epoch())
|
||||
.count(),
|
||||
@@ -167,22 +167,32 @@ void Cubic::setAppLimited(bool limited, TimePoint eventTime) noexcept {
|
||||
steadyState_.lastReductionTime->time_since_epoch())
|
||||
.count()
|
||||
: -1);
|
||||
bool currentAppLimited = isAppLimited();
|
||||
if (!currentAppLimited && limited) {
|
||||
bool currentAppIdle = isAppIdle();
|
||||
if (!currentAppIdle && idle) {
|
||||
quiescenceStart_ = eventTime;
|
||||
}
|
||||
if (!limited && currentAppLimited && *quiescenceStart_ <= eventTime &&
|
||||
if (!idle && currentAppIdle && *quiescenceStart_ <= eventTime &&
|
||||
steadyState_.lastReductionTime) {
|
||||
*steadyState_.lastReductionTime +=
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
eventTime - *quiescenceStart_);
|
||||
}
|
||||
if (!limited) {
|
||||
if (!idle) {
|
||||
quiescenceStart_ = folly::none;
|
||||
}
|
||||
}
|
||||
|
||||
void Cubic::setAppLimited() {
|
||||
// we use app-idle for Cubic
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
@@ -97,7 +97,8 @@ class Cubic : public CongestionController {
|
||||
uint64_t getCongestionWindow() const noexcept override;
|
||||
bool canBePaced() const 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;
|
||||
|
||||
@@ -116,6 +117,7 @@ class Cubic : public CongestionController {
|
||||
CubicStates state_{CubicStates::Hystart};
|
||||
|
||||
private:
|
||||
bool isAppIdle() const noexcept;
|
||||
void onPacketAcked(const AckEvent& ack);
|
||||
void onPacketAckedInHystart(const AckEvent& ack);
|
||||
void onPacketAckedInSteady(const AckEvent& ack);
|
||||
|
@@ -124,7 +124,7 @@ TEST_F(CubicTest, CwndIncreaseAfterReduction) {
|
||||
EXPECT_EQ(CubicStates::Steady, cubic.state());
|
||||
}
|
||||
|
||||
TEST_F(CubicTest, AppLimited) {
|
||||
TEST_F(CubicTest, AppIdle) {
|
||||
QuicConnectionStateBase conn(QuicNodeType::Client);
|
||||
conn.udpSendPacketLen = 1500;
|
||||
TestingCubic cubic(conn);
|
||||
@@ -150,7 +150,7 @@ TEST_F(CubicTest, AppLimited) {
|
||||
EXPECT_GT(cubic.getCongestionWindow(), cwnd);
|
||||
cwnd = cubic.getCongestionWindow();
|
||||
|
||||
cubic.setAppLimited(true, reductionTime + 1100ms);
|
||||
cubic.setAppIdle(true, reductionTime + 1100ms);
|
||||
EXPECT_TRUE(cubic.isAppLimited());
|
||||
auto packet2 = makeTestingWritePacket(2, 1000, 3000);
|
||||
cubic.onPacketSent(packet2);
|
||||
@@ -159,7 +159,7 @@ TEST_F(CubicTest, AppLimited) {
|
||||
EXPECT_EQ(cubic.getCongestionWindow(), cwnd);
|
||||
|
||||
// 1 seconds of quiescence
|
||||
cubic.setAppLimited(false, reductionTime + 2100ms);
|
||||
cubic.setAppIdle(false, reductionTime + 2100ms);
|
||||
EXPECT_FALSE(cubic.isAppLimited());
|
||||
auto packet3 = makeTestingWritePacket(3, 1000, 4000);
|
||||
cubic.onPacketSent(packet3);
|
||||
|
@@ -153,7 +153,7 @@ QuicStreamManager::getOrCreateOpenedLocalStream(StreamId streamId) {
|
||||
QuicStreamState* QuicStreamManager::getStream(StreamId streamId) {
|
||||
if (isRemoteStream(nodeType_, streamId)) {
|
||||
auto stream = getOrCreatePeerStream(streamId);
|
||||
updateAppLimitedState();
|
||||
updateAppIdleState();
|
||||
return stream;
|
||||
}
|
||||
auto it = streams_.find(streamId);
|
||||
@@ -169,7 +169,7 @@ QuicStreamState* QuicStreamManager::getStream(StreamId streamId) {
|
||||
"Trying to get unopened local stream",
|
||||
TransportErrorCode::STREAM_STATE_ERROR);
|
||||
}
|
||||
updateAppLimitedState();
|
||||
updateAppIdleState();
|
||||
return stream;
|
||||
}
|
||||
|
||||
@@ -298,7 +298,7 @@ QuicStreamManager::createStream(StreamId streamId) {
|
||||
std::forward_as_tuple(streamId),
|
||||
std::forward_as_tuple(streamId, conn_));
|
||||
QUIC_STATS(conn_.infoCallback, onNewQuicStream);
|
||||
updateAppLimitedState();
|
||||
updateAppIdleState();
|
||||
return &it.first->second;
|
||||
}
|
||||
|
||||
@@ -342,7 +342,7 @@ void QuicStreamManager::removeClosedStream(StreamId streamId) {
|
||||
openLocalStreams_.erase(streamItr);
|
||||
}
|
||||
}
|
||||
updateAppLimitedState();
|
||||
updateAppIdleState();
|
||||
}
|
||||
|
||||
void QuicStreamManager::updateLossStreams(QuicStreamState& stream) {
|
||||
@@ -403,18 +403,18 @@ void QuicStreamManager::updatePeekableStreams(QuicStreamState& stream) {
|
||||
}
|
||||
}
|
||||
|
||||
void QuicStreamManager::updateAppLimitedState() {
|
||||
void QuicStreamManager::updateAppIdleState() {
|
||||
bool currentNonCtrlStreams = hasNonCtrlStreams();
|
||||
if (isAppLimited_ && !currentNonCtrlStreams) {
|
||||
if (isAppIdle_ && !currentNonCtrlStreams) {
|
||||
// We were app limited, and we continue to be app limited.
|
||||
return;
|
||||
} else if (!isAppLimited_ && currentNonCtrlStreams) {
|
||||
} else if (!isAppIdle_ && currentNonCtrlStreams) {
|
||||
// We were not app limited, and we continue to be not app limited.
|
||||
return;
|
||||
}
|
||||
isAppLimited_ = !currentNonCtrlStreams;
|
||||
isAppIdle_ = !currentNonCtrlStreams;
|
||||
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;
|
||||
numControlStreams_++;
|
||||
}
|
||||
updateAppLimitedState();
|
||||
updateAppIdleState();
|
||||
}
|
||||
|
||||
bool QuicStreamManager::isAppLimited() const {
|
||||
return isAppLimited_;
|
||||
bool QuicStreamManager::isAppIdle() const {
|
||||
return isAppIdle_;
|
||||
}
|
||||
} // namespace quic
|
||||
|
@@ -563,16 +563,16 @@ class QuicStreamManager {
|
||||
dataRejectedStreams_.clear();
|
||||
}
|
||||
|
||||
bool isAppLimited() const;
|
||||
bool isAppIdle() const;
|
||||
|
||||
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.
|
||||
// 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
|
||||
// there were no non-control streams before and there is at least one at the
|
||||
// time of calling
|
||||
void updateAppLimitedState();
|
||||
void updateAppIdleState();
|
||||
|
||||
QuicStreamState* FOLLY_NULLABLE
|
||||
getOrCreateOpenedLocalStream(StreamId streamId);
|
||||
@@ -666,8 +666,8 @@ class QuicStreamManager {
|
||||
// Data structure to keep track of stream that have detected lost data
|
||||
std::vector<StreamId> lossStreams_;
|
||||
|
||||
// Record whether or not we are app limited.
|
||||
bool isAppLimited_{false};
|
||||
// Record whether or not we are app-idle.
|
||||
bool isAppIdle_{false};
|
||||
};
|
||||
|
||||
} // namespace quic
|
||||
|
@@ -218,15 +218,21 @@ struct CongestionController {
|
||||
* example, we probably don't want to pace a connection in Recovery.
|
||||
*/
|
||||
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
|
||||
* 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;
|
||||
|
||||
/**
|
||||
@@ -246,8 +252,7 @@ struct CongestionController {
|
||||
|
||||
/**
|
||||
* Whether the congestion controller thinks it's currently in app-limited
|
||||
* state. For some congestion controllers, e.g. BBR, the app-limited state may
|
||||
* not be the same as the app.
|
||||
* state.
|
||||
*/
|
||||
virtual bool isAppLimited() const = 0;
|
||||
};
|
||||
|
@@ -37,7 +37,8 @@ class MockCongestionController : public CongestionController {
|
||||
,
|
||||
setMinimalPacingInterval,
|
||||
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());
|
||||
};
|
||||
} // namespace test
|
||||
|
@@ -44,140 +44,140 @@ class QuicStreamManagerTest : public Test {
|
||||
MockCongestionController* mockController;
|
||||
};
|
||||
|
||||
TEST_F(QuicStreamManagerTest, TestAppLimitedCreateBidiStream) {
|
||||
TEST_F(QuicStreamManagerTest, TestAppIdleCreateBidiStream) {
|
||||
auto& manager = *conn.streamManager;
|
||||
EXPECT_FALSE(manager.isAppLimited());
|
||||
EXPECT_FALSE(manager.isAppIdle());
|
||||
|
||||
// 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();
|
||||
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
|
||||
stream.value()->send.state = StreamSendStates::Closed();
|
||||
stream.value()->recv.state = StreamReceiveStates::Closed();
|
||||
manager.removeClosedStream(stream.value()->id);
|
||||
EXPECT_TRUE(manager.isAppLimited());
|
||||
EXPECT_TRUE(manager.isAppIdle());
|
||||
EXPECT_EQ(manager.getStream(id), nullptr);
|
||||
}
|
||||
|
||||
TEST_F(QuicStreamManagerTest, TestAppLimitedCreateUnidiStream) {
|
||||
TEST_F(QuicStreamManagerTest, TestAppIdleCreateUnidiStream) {
|
||||
auto& manager = *conn.streamManager;
|
||||
EXPECT_FALSE(manager.isAppLimited());
|
||||
EXPECT_CALL(*mockController, setAppLimited(false, _)).Times(0);
|
||||
EXPECT_FALSE(manager.isAppIdle());
|
||||
EXPECT_CALL(*mockController, setAppIdle(false, _)).Times(0);
|
||||
auto stream = manager.createNextUnidirectionalStream();
|
||||
EXPECT_FALSE(manager.isAppLimited());
|
||||
EXPECT_FALSE(manager.isAppIdle());
|
||||
|
||||
// Force transition to closed state
|
||||
EXPECT_CALL(*mockController, setAppLimited(true, _));
|
||||
EXPECT_CALL(*mockController, setAppIdle(true, _));
|
||||
stream.value()->send.state = StreamSendStates::Closed();
|
||||
stream.value()->recv.state = StreamReceiveStates::Closed();
|
||||
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;
|
||||
EXPECT_FALSE(manager.isAppLimited());
|
||||
EXPECT_CALL(*mockController, setAppLimited(false, _)).Times(0);
|
||||
EXPECT_FALSE(manager.isAppIdle());
|
||||
EXPECT_CALL(*mockController, setAppIdle(false, _)).Times(0);
|
||||
|
||||
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());
|
||||
EXPECT_TRUE(manager.isAppLimited());
|
||||
EXPECT_TRUE(manager.isAppIdle());
|
||||
|
||||
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;
|
||||
EXPECT_FALSE(manager.isAppLimited());
|
||||
EXPECT_FALSE(manager.isAppIdle());
|
||||
|
||||
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());
|
||||
EXPECT_TRUE(manager.isAppLimited());
|
||||
EXPECT_TRUE(manager.isAppIdle());
|
||||
|
||||
EXPECT_CALL(*mockController, setAppLimited(false, _));
|
||||
EXPECT_CALL(*mockController, setAppIdle(false, _));
|
||||
manager.createNextUnidirectionalStream();
|
||||
EXPECT_FALSE(manager.isAppLimited());
|
||||
EXPECT_FALSE(manager.isAppIdle());
|
||||
}
|
||||
|
||||
TEST_F(QuicStreamManagerTest, TestAppLimitedCreatePeerStream) {
|
||||
TEST_F(QuicStreamManagerTest, TestAppIdleCreatePeerStream) {
|
||||
auto& manager = *conn.streamManager;
|
||||
EXPECT_FALSE(manager.isAppLimited());
|
||||
EXPECT_FALSE(manager.isAppIdle());
|
||||
StreamId id = 0;
|
||||
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);
|
||||
EXPECT_TRUE(manager.isAppLimited());
|
||||
EXPECT_TRUE(manager.isAppIdle());
|
||||
|
||||
EXPECT_CALL(*mockController, setAppLimited(false, _));
|
||||
EXPECT_CALL(*mockController, setAppIdle(false, _));
|
||||
StreamId id2 = 4;
|
||||
manager.getStream(id2);
|
||||
EXPECT_FALSE(manager.isAppLimited());
|
||||
EXPECT_FALSE(manager.isAppIdle());
|
||||
}
|
||||
|
||||
TEST_F(QuicStreamManagerTest, TestAppLimitedExistingPeerStream) {
|
||||
TEST_F(QuicStreamManagerTest, TestAppIdleExistingPeerStream) {
|
||||
auto& manager = *conn.streamManager;
|
||||
EXPECT_FALSE(manager.isAppLimited());
|
||||
EXPECT_CALL(*mockController, setAppLimited(false, _)).Times(0);
|
||||
EXPECT_FALSE(manager.isAppIdle());
|
||||
EXPECT_CALL(*mockController, setAppIdle(false, _)).Times(0);
|
||||
|
||||
StreamId id = 0;
|
||||
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);
|
||||
EXPECT_TRUE(manager.isAppLimited());
|
||||
EXPECT_TRUE(manager.isAppIdle());
|
||||
|
||||
manager.getStream(id);
|
||||
EXPECT_TRUE(manager.isAppLimited());
|
||||
EXPECT_TRUE(manager.isAppIdle());
|
||||
}
|
||||
|
||||
TEST_F(QuicStreamManagerTest, TestAppLimitedClosePeerStream) {
|
||||
TEST_F(QuicStreamManagerTest, TestAppIdleClosePeerStream) {
|
||||
auto& manager = *conn.streamManager;
|
||||
EXPECT_FALSE(manager.isAppLimited());
|
||||
EXPECT_FALSE(manager.isAppIdle());
|
||||
StreamId id = 0;
|
||||
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
|
||||
stream->send.state = StreamSendStates::Closed();
|
||||
stream->recv.state = StreamReceiveStates::Closed();
|
||||
manager.removeClosedStream(stream->id);
|
||||
EXPECT_TRUE(manager.isAppLimited());
|
||||
EXPECT_TRUE(manager.isAppIdle());
|
||||
EXPECT_EQ(manager.getStream(id), nullptr);
|
||||
}
|
||||
|
||||
TEST_F(QuicStreamManagerTest, TestAppLimitedCloseControlStream) {
|
||||
TEST_F(QuicStreamManagerTest, TestAppIdleCloseControlStream) {
|
||||
auto& manager = *conn.streamManager;
|
||||
EXPECT_FALSE(manager.isAppLimited());
|
||||
EXPECT_CALL(*mockController, setAppLimited(false, _)).Times(0);
|
||||
EXPECT_FALSE(manager.isAppIdle());
|
||||
EXPECT_CALL(*mockController, setAppIdle(false, _)).Times(0);
|
||||
|
||||
StreamId id = 0;
|
||||
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);
|
||||
EXPECT_TRUE(manager.isAppLimited());
|
||||
EXPECT_TRUE(manager.isAppIdle());
|
||||
|
||||
// Force transition to closed state
|
||||
stream->send.state = StreamSendStates::Closed();
|
||||
stream->recv.state = StreamReceiveStates::Closed();
|
||||
manager.removeClosedStream(stream->id);
|
||||
EXPECT_TRUE(manager.isAppLimited());
|
||||
EXPECT_TRUE(manager.isAppIdle());
|
||||
}
|
||||
} // namespace test
|
||||
} // namespace quic
|
||||
|
Reference in New Issue
Block a user