1
0
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:
Yang Chi
2019-05-24 12:11:50 -07:00
committed by Facebook Github Bot
parent 41a6b82538
commit 1dbe39fd76
16 changed files with 233 additions and 106 deletions

View File

@@ -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

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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;
};

View File

@@ -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

View File

@@ -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